// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2022 - 2025 Mucse Corporation. */

#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/sched.h>

#include "rnpgbe.h"
#include "rnpgbe_phy.h"
#include "rnpgbe_mbx.h"
#include "rnpgbe_ethtool.h"
#include "rnpgbe_sriov.h"

#ifdef HAVE_ETHTOOL_GET_SSET_COUNT

enum rnpgbe_priv_bits {
	rnpgbe_mac_loopback = 0,
	rnpgbe_padding_enable = 8,
};

static const char rnpgbe_priv_flags_strings[][ETH_GSTRING_LEN] = {
//#define RNPGBE_MAC_LOOPBACK BIT(0)
#define RNPGBE_FORCE_CLOSE BIT(0)
#define RNPGBE_TX_SOLF_PADDING BIT(1)
#define RNPGBE_PADDING_DEBUG BIT(2)
#define RNPGBE_SIMULATE_DOWN BIT(3)
#define RNPGBE_ULTRA_SHORT BIT(4)
#define RNPGBE_DOUBLE_VLAN BIT(5)
#define RNPGBE_PAUSE_OWN BIT(6)
#define RNPGBE_STAGS_ENABLE BIT(7)
#define RNPGBE_JUMBO_ENABLE BIT(8)
#define RNPGBE_TX_PADDING BIT(9)
#define RNPGBE_REC_HDR_LEN_ERR BIT(10)
#define RNPGBE_DOUBLE_VLAN_RECEIVE BIT(11)
#define RNPGBE_RX_SKIP_EN BIT(12)
#define RNPGBE_TCP_SYNC_PRIO BIT(13)
#define RNPGBE_REMAP_PRIO BIT(14)
#define RNPGBE_8023_PRIO BIT(15)
#define RNPGBE_SRIOV_VLAN_MODE BIT(16)
#define RNPGBE_LLDP_EN BIT(17)
	"link_down_on_close",
//	"mac_loopback",
	"soft_tx_padding_off",
	"padding_debug",
	"simulate_link_down",
	"ultra_short_packet",
	"double_vlan",
	"pause_use_own_address",
	"stags_enable",
	"jumbo_enable",
	"mac_tx_padding_off",
	"mask_len_err",
	"double_vlan_receive",
	"rx_skip_en",
	"tcp_sync_prio",
	"remap_prio",
	"8023_prio",
	"sriov_vlan_mode",
	"lldp_en",
};

#define RNPGBE_PRIV_FLAGS_STR_LEN ARRAY_SIZE(rnpgbe_priv_flags_strings)
#endif

/* setup queue speed limit to max_rate */
static void rnpgbe_dma_set_tx_maxrate(struct rnpgbe_dma_info *dma,
				      u16 queue, u32 max_rate)
{
	/* todo */
}

/* setup mac with vf_num to veb table */
static void rnpgbe_dma_set_veb_mac(struct rnpgbe_dma_info *dma, u8 *mac,
				   u32 vfnum, u32 ring)
{
	u32 maclow, machi, ring_vfnum;
	int port;

	maclow = (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
	machi = (mac[0] << 8) | mac[1];
	ring_vfnum = ring | ((0x80 | vfnum) << 8);
	for (port = 0; port < 1; port++) {
		dma_wr32(dma, RNPGBE_DMA_PORT_VBE_MAC_LO_TBL(port, vfnum),
			 maclow);
		dma_wr32(dma, RNPGBE_DMA_PORT_VBE_MAC_HI_TBL(port, vfnum),
			 machi);
		dma_wr32(dma, RNPGBE_DMA_PORT_VEB_VF_RING_TBL(port, vfnum),
			 ring_vfnum);
	}
}

/* setup vlan with vf_num to veb table */
static void rnpgbe_dma_set_veb_vlan(struct rnpgbe_dma_info *dma, u16 vlan,
				    u32 vfnum)
{
	int port;

	/* each vf can support only one vlan */
	for (port = 0; port < 1; port++)
		dma_wr32(dma, RNPGBE_DMA_PORT_VEB_VID_TBL(port, vfnum), vlan);
}

static void rnpgbe_dma_set_veb_vlan_mask(struct rnpgbe_dma_info *dma,
					 u16 vlan, u16 mask, int entry)
{
	/* bit 19:12 is mask bit 11:0 is vid */
	dma_wr32(dma, RNPGBE_DMA_PORT_VEB_VID_TBL(0, entry),
		 (mask << 12) | vlan);
}

static void rnpgbe_dma_clr_veb_all(struct rnpgbe_dma_info *dma)
{
	int port, i;

	for (port = 0; port < 1; port++) {
		for (i = 0; i < RNPGBE_VEB_TBL_CNTS; i++) {
			dma_wr32(dma, RNPGBE_DMA_PORT_VBE_MAC_LO_TBL(port, i),
				 0);
			dma_wr32(dma, RNPGBE_DMA_PORT_VBE_MAC_HI_TBL(port, i),
				 0);
			dma_wr32(dma, RNPGBE_DMA_PORT_VEB_VID_TBL(port, i), 0);
			dma_wr32(dma, RNPGBE_DMA_PORT_VEB_VF_RING_TBL(port, i),
				 0);
		}
	}
}

static struct rnpgbe_dma_operations dma_ops_rnpgbe = {
	.set_tx_maxrate = &rnpgbe_dma_set_tx_maxrate,
	.set_veb_mac = &rnpgbe_dma_set_veb_mac,
	.set_veb_vlan = &rnpgbe_dma_set_veb_vlan,
	.set_veb_vlan_mask = &rnpgbe_dma_set_veb_vlan_mask,
	.clr_veb_all = &rnpgbe_dma_clr_veb_all,

};

/**
 *  rnpgbe_eth_set_rar - Set Rx address register
 *  @eth: pointer to eth structure
 *  @index: Receive address register to write
 *  @addr: Address to put into receive address register
 *  @enable_addr: set flag that address is active
 *
 *  Puts an ethernet address into a receive address register.
 **/
static s32 rnpgbe_eth_set_rar(struct rnpgbe_eth_info *eth,
			      u32 index, u8 *addr,
			      bool enable_addr)
{
	u32 rar_entries = eth->num_rar_entries;
	u32 rar_low, rar_high = 0;
	u32 mcstctrl;

	/* Make sure we are using a valid rar index range */
	if (index >= rar_entries) {
		rnpgbe_err("RAR index %d is out of range.\n", index);
		return RNP_ERR_INVALID_ARGUMENT;
	}

	eth_dbg(eth, "    RAR[%d] <= %pM.  vmdq:%d enable:0x%x\n", index, addr);

	/*
	 * HW expects these in big endian so we reverse the byte
	 * order from network order (big endian) to little endian
	 */
	rar_low = ((u32)addr[5] | ((u32)addr[4] << 8) | ((u32)addr[3] << 16) |
		   ((u32)addr[2] << 24));
	/*
	 * Some parts put the VMDq setting in the extra RAH bits,
	 * so save everything except the lower 16 bits that hold part
	 * of the address and the address valid bit.
	 */
	rar_high = eth_rd32(eth, RNPGBE_ETH_RAR_RH(index));
	rar_high &= ~(0x0000FFFF | RNPGBE_RAH_AV);
	rar_high |= ((u32)addr[1] | ((u32)addr[0] << 8));

	if (enable_addr)
		rar_high |= RNPGBE_RAH_AV;

	eth_wr32(eth, RNPGBE_ETH_RAR_RL(index), rar_low);
	eth_wr32(eth, RNPGBE_ETH_RAR_RH(index), rar_high);

	/* open unicast filter */
	/* we now not use unicast */
	/* but we must open this since dest-mac filter | unicast table */
	/* all packets up if close unicast table */
	mcstctrl = eth_rd32(eth, RNPGBE_ETH_DMAC_MCSTCTRL);
	mcstctrl |= RNPGBE_MCSTCTRL_UNICASE_TBL_EN;
	eth_wr32(eth, RNPGBE_ETH_DMAC_MCSTCTRL, mcstctrl);

	return 0;
}

/**
 *  rnpgbe_eth_clear_rar - Remove Rx address register
 *  @eth: pointer to eth structure
 *  @index: Receive address register to write
 *
 *  Clears an ethernet address from a receive address register.
 **/
static s32 rnpgbe_eth_clear_rar(struct rnpgbe_eth_info *eth,
				u32 index)
{
	u32 rar_entries = eth->num_rar_entries;
	u32 rar_high;

	/* Make sure we are using a valid rar index range */
	if (index >= rar_entries) {
		eth_dbg(eth, "RAR index %d is out of range.\n", index);
		return RNP_ERR_INVALID_ARGUMENT;
	}

	/*
	 * Some parts put the VMDq setting in the extra RAH bits,
	 * so save everything except the lower 16 bits that hold part
	 * of the address and the address valid bit.
	 */
	rar_high = eth_rd32(eth, RNPGBE_ETH_RAR_RH(index));
	rar_high &= ~(0x0000FFFF | RNPGBE_RAH_AV);
	eth_wr32(eth, RNPGBE_ETH_RAR_RL(index), 0);
	eth_wr32(eth, RNPGBE_ETH_RAR_RH(index), rar_high);

	/* clear VMDq pool/queue selection for this RAR */
	eth->ops.clear_vmdq(eth, index, RNP_CLEAR_VMDQ_ALL);

	return 0;
}

/**
 *  rnpgbe_eth_set_vmdq - Associate a VMDq pool index with a rx address
 *  @eth: pointer to eth struct
 *  @rar: receive address register index to associate with a VMDq index
 *  @vmdq: VMDq pool index
 *  only mac->vf
 **/
static s32 rnpgbe_eth_set_vmdq(struct rnpgbe_eth_info *eth,
			       u32 rar, u32 vmdq)
{
	u32 rar_entries = eth->num_rar_entries;

	/* Make sure we are using a valid rar index range */
	if (rar >= rar_entries) {
		eth_dbg(eth, "RAR index %d is out of range.\n", rar);
		return RNP_ERR_INVALID_ARGUMENT;
	}

	eth_wr32(eth, RNPGBE_VM_DMAC_MPSAR_RING(rar), vmdq);

	return 0;
}

/**
 *  rnpgbe_eth_clear_vmdq - Disassociate a VMDq pool index from a rx address
 *  @eth: pointer to eth struct
 *  @rar: receive address register index to disassociate
 *  @vmdq: VMDq pool index to remove from the rar
 **/
static s32 rnpgbe_eth_clear_vmdq(struct rnpgbe_eth_info *eth,
				 u32 rar, u32 vmdq)
{
	u32 rar_entries = eth->num_rar_entries;

	/* Make sure we are using a valid rar index range */
	if (rar >= rar_entries) {
		eth_dbg(eth, "RAR index %d is out of range.\n", rar);
		return RNP_ERR_INVALID_ARGUMENT;
	}

	eth_wr32(eth, RNPGBE_VM_DMAC_MPSAR_RING(rar), 0);

	return 0;
}

static s32 rnpgbe_mta_vector(struct rnpgbe_eth_info *eth, u8 *mc_addr)
{
	u32 vector = 0;

	switch (eth->mc_filter_type) {
	case 0: /* use bits [36:47] of the address */
		vector = ((mc_addr[4] << 8) | (((u16)mc_addr[5])));
		break;
	case 1: /* use bits [35:46] of the address */
		vector = ((mc_addr[4] << 7) | (((u16)mc_addr[5]) >> 1));
		break;
	case 2: /* use bits [34:45] of the address */
		vector = ((mc_addr[4] << 6) | (((u16)mc_addr[5]) >> 2));
		break;
	case 3: /* use bits [32:43] of the address */
		vector = ((mc_addr[4] << 5) | (((u16)mc_addr[5]) >> 3));
		break;
	case 4: /* use bits [32:43] of the address */
		vector = ((mc_addr[0] << 8) | (((u16)mc_addr[1])));
		vector = (vector >> 4);
		break;
	case 5: /* use bits [32:43] of the address */
		vector = ((mc_addr[0] << 8) | (((u16)mc_addr[1])));
		vector = (vector >> 3);
		break;
	case 6: /* use bits [32:43] of the address */
		vector = ((mc_addr[0] << 8) | (((u16)mc_addr[1])));
		vector = (vector >> 2);
		break;
	case 7: /* use bits [32:43] of the address */
		vector = ((mc_addr[0] << 8) | (((u16)mc_addr[1])));
		break;
	default: /* Invalid mc_filter_type */
		hw_dbg(hw, "MC filter type param set incorrectly\n");
		break;
	}

	/* vector can only be 12-bits or boundary will be exceeded */
	vector &= 0xFFF;
	return vector;
}

static void rnpgbe_set_mta(struct rnpgbe_hw *hw, u8 *mc_addr)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	u32 vector_bit;
	u32 vector_reg;
	u32 vector;

	hw->addr_ctrl.mta_in_use++;

	vector = rnpgbe_mta_vector(eth, mc_addr);

	/*
	 * The MTA is a register array of 128 32-bit registers. It is treated
	 * like an array of 4096 bits.  We want to set bit
	 * BitArray[vector_value]. So we figure out what register the bit is
	 * in, read it, OR in the new bit, then write back the new value.  The
	 * register is determined by the upper 7 bits of the vector value and
	 * the bit within that register are determined by the lower 5 bits of
	 * the value.
	 */
	vector_reg = (vector >> 5) & 0x7F;
	vector_bit = vector & 0x1F;
	hw_dbg(hw, "\t\t%pM: MTA-BIT:%4d, MTA_REG[%d][%d] <= 1\n", mc_addr,
	       vector, vector_reg, vector_bit);
	eth->mta_shadow[vector_reg] |= (1 << vector_bit);
}

static void rnpgbe_set_vf_mta(struct rnpgbe_hw *hw, u16 vector)
{
	/* vf/pf use the same multicast table */
	struct rnpgbe_eth_info *eth = &hw->eth;
	u32 vector_bit;
	u32 vector_reg;

	hw->addr_ctrl.mta_in_use++;

	vector_reg = (vector >> 5) & 0x7F;
	vector_bit = vector & 0x1F;
	hw_dbg(hw, "\t\t vf M: MTA-BIT:%4d, MTA_REG[%d][%d] <= 1\n", vector,
	       vector_reg, vector_bit);
	eth->mta_shadow[vector_reg] |= (1 << vector_bit);
}

static u8 *rnpgbe_addr_list_itr(struct rnpgbe_hw __maybe_unused *hw,
				u8 **mc_addr_ptr)
{
	struct netdev_hw_addr *mc_ptr;
	u8 *addr = *mc_addr_ptr;

	mc_ptr = container_of(addr, struct netdev_hw_addr, addr[0]);
	if (mc_ptr->list.next) {
		struct netdev_hw_addr *ha;

		ha = list_entry(mc_ptr->list.next, struct netdev_hw_addr, list);
		*mc_addr_ptr = ha->addr;
	} else {
		*mc_addr_ptr = NULL;
	}

	return addr;
}

/**
 *  rnpgbe_eth_update_mc_addr_list - Updates MAC list of multicast addresses
 *  @eth: pointer to eth structure
 *  @netdev: pointer to net device structure
 *  @sriov_on: sriov flags
 *
 *  The given list replaces any existing list. Clears the MC addrs from receive
 *  address registers and the multicast table. Uses unused receive address
 *  registers for the first multicast addresses, and hashes the rest into the
 *  multicast table.
 **/
static s32 rnpgbe_eth_update_mc_addr_list(struct rnpgbe_eth_info *eth,
					  struct net_device *netdev,
					  bool sriov_on)
{
	struct rnpgbe_hw *hw = (struct rnpgbe_hw *)eth->back;
#ifdef NETDEV_HW_ADDR_T_MULTICAST
	struct netdev_hw_addr *ha;
#endif /* NETDEV_HW_ADDR_T_MULTICAST */
	u8 *addr_list = NULL;
	int addr_count = 0;
	u8 ncsi_mc_addr[6];
	int ret;
	u32 i;
	u32 v;

	/*
	 * Set the new number of MC addresses that we are being requested to
	 * use.
	 */
	hw->addr_ctrl.num_mc_addrs = netdev_mc_count(netdev);
	hw->addr_ctrl.mta_in_use = 0;

	/* Clear mta_shadow */
	eth_dbg(eth, " Clearing MTA(multicast table)\n");
	memset(&eth->mta_shadow, 0, sizeof(eth->mta_shadow));

	/* Update mta shadow */
	eth_dbg(eth, " Updating MTA..\n");
	addr_count = netdev_mc_count(netdev);

#ifdef NETDEV_HW_ADDR_T_MULTICAST
	ha = list_first_entry(&netdev->mc.list, struct netdev_hw_addr, list);
	addr_list = ha->addr;
#else /* NETDEV_HW_ADDR_T_MULTICAST */
	addr_list = netdev->mc_list->dmi_addr;
#endif /* NETDEV_HW_ADDR_T_MULTICAST */
	for (i = 0; i < addr_count; i++) {
		eth_dbg(eth, " Adding the multicast addresses:\n");
		rnpgbe_set_mta(hw, rnpgbe_addr_list_itr(hw, &addr_list));
	}

	if (sriov_on) {
		struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;

		for (i = 0; i < adapter->num_vfs; i++) {
			struct vf_data_storage *vfinfo;
			int j;

			if (!adapter->vfinfo)
				break;
			vfinfo =  &adapter->vfinfo[i];
			for (j = 0; j < vfinfo->num_vf_mc_hashes; j++)
				rnpgbe_set_vf_mta(hw, vfinfo->vf_mc_hashes[j]);
		}
	}
	/* update ncsi multicast address */
	for (i = NCSI_RAR_NUM; i < NCSI_MC_NUM; i++) {
		ret = hw->ops.get_ncsi_mac(hw, ncsi_mc_addr, i);
		if (!ret)
			rnpgbe_set_mta(hw, ncsi_mc_addr);
	}

	/* Enable mta */
	for (i = 0; i < hw->eth.mcft_size; i++) {
		if (hw->addr_ctrl.mta_in_use) {
			eth_wr32(eth, RNPGBE_ETH_MUTICAST_HASH_TABLE(i),
				 eth->mta_shadow[i]);
		}
	}

	if (hw->addr_ctrl.mta_in_use > 0) {
		v = eth_rd32(eth, RNPGBE_ETH_DMAC_MCSTCTRL);
		eth_wr32(eth, RNPGBE_ETH_DMAC_MCSTCTRL,
			 v | RNPGBE_MCSTCTRL_MULTICASE_TBL_EN |
				 eth->mc_filter_type);
	}

	eth_dbg(eth, " update MTA Done. mta_in_use:%d\n",
		hw->addr_ctrl.mta_in_use);
	return hw->addr_ctrl.mta_in_use;
}

/* clean all mc addr */
static void rnpgbe_eth_clr_mc_addr(struct rnpgbe_eth_info *eth)
{
	int i;

	for (i = 0; i < eth->mcft_size; i++)
		eth_wr32(eth, RNPGBE_ETH_MUTICAST_HASH_TABLE(i), 0);
}

/**
 *  rnpgbe_eth_set_rss_hfunc - Remove Rx address register
 *  @eth: pointer to eth structure
 *  @hfunc: type
 *
 *  update rss key to eth regs
 **/
static int rnpgbe_eth_set_rss_hfunc(struct rnpgbe_eth_info *eth, int hfunc)
{
	u32 data;

	data = eth_rd32(eth, RNPGBE_ETH_RSS_CONTROL);
	/* clean mode only bit[14:15] */
	data &= ~(BIT(14) | BIT(15));

	if (hfunc == rss_func_top)
		;
	else if (hfunc == rss_func_xor)
		data |= BIT(14);
	else if (hfunc == rss_func_order)
		data |= BIT(15);
	else
		return -EINVAL;

	/* update to hardware */
	eth_wr32(eth, RNPGBE_ETH_RSS_CONTROL, data);

	return 0;
}

/**
 *  rnpgbe_eth_update_rss_key - Remove Rx address register
 *  @eth: pointer to eth structure
 *  @sriov_flag: sriov status
 *
 *  update rss key to eth regs
 **/

static void rnpgbe_eth_update_rss_key(struct rnpgbe_eth_info *eth,
				      bool sriov_flag)
{
	struct rnpgbe_hw *hw = (struct rnpgbe_hw *)eth->back;
	u32 iov_en = (sriov_flag) ? RNPGBE_IOV_ENABLED : 0;
	int key_len = RNP_RSS_KEY_SIZE;
	u8 key_temp[RNP_RSS_KEY_SIZE];
	u8 *key = hw->rss_key;
	u32 *value;
	u32 data;
	int i;

	data = eth_rd32(eth, RNPGBE_ETH_RSS_CONTROL);

	/* reorder the key */
	for (i = 0; i < key_len; i++)
		*(key_temp + key_len - i - 1) = *(key + i);

	value = (u32 *)key_temp;

	for (i = 0; i < key_len; i = i + 4)
		eth_wr32(eth, RNPGBE_ETH_RSS_KEY + i, *(value + i / 4));

	data |= (RNPGBE_ETH_ENABLE_RSS_ONLY | iov_en);
	eth_wr32(eth, RNPGBE_ETH_RSS_CONTROL, data);
}

/**
 *  rnpgbe_eth_update_rss_table - Remove Rx address register
 *  @eth: pointer to eth structure
 *
 *  update rss table to eth regs
 **/
static void rnpgbe_eth_update_rss_table(struct rnpgbe_eth_info *eth)
{
	struct rnpgbe_hw *hw = (struct rnpgbe_hw *)eth->back;
	u32 reta_entries = hw->rss_indir_tbl_num;
	u32 tc_entries = hw->rss_tc_tbl_num;
	int i;

	for (i = 0; i < tc_entries; i++)
		eth_wr32(eth, RNPGBE_ETH_TC_IPH_OFFSET_TABLE(i),
			 hw->rss_tc_tbl[i]);

	for (i = 0; i < reta_entries; i++)
		eth_wr32(eth, RNPGBE_ETH_RSS_INDIR_TBL(i),
			 hw->rss_indir_tbl[i]);

	/* if we update rss table,
	 * we should update default ring same with rss[0]
	 */
	eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_RING, hw->rss_indir_tbl[0]);
}

/**
 *  rnpgbe_eth_set_vfta - Set VLAN filter table
 *  @eth: pointer to eth structure
 *  @vlan: VLAN id to write to VLAN filter
 *  @vlan_on: boolean flag to turn on/off VLAN in VFVF
 *
 *  Turn on/off specified VLAN in the VLAN filter table.
 **/
static s32 rnpgbe_eth_set_vfta(struct rnpgbe_eth_info *eth, u32 vlan,
			       bool vlan_on)
{
	bool vfta_changed = false;
	u32 targetbit;
	s32 regindex;
	u32 bitindex;
	u32 vfta;

	/* todo in vf mode vlvf register can be set according to vind*/
	if (vlan > 4095)
		return RNP_ERR_PARAM;

	regindex = (vlan >> 5) & 0x7F;
	bitindex = vlan & 0x1F;
	targetbit = (1 << bitindex);
	vfta = eth_rd32(eth, RNPGBE_VFTA(regindex));

	if (vlan_on) {
		if (!(vfta & targetbit)) {
			vfta |= targetbit;
			vfta_changed = true;
		}
	} else {
		if ((vfta & targetbit)) {
			vfta &= ~targetbit;
			vfta_changed = true;
		}
	}

	if (vfta_changed)
		eth_wr32(eth, RNPGBE_VFTA(regindex), vfta);

	return 0;
}

static void rnpgbe_eth_clr_vfta(struct rnpgbe_eth_info *eth)
{
	u32 offset;

	for (offset = 0; offset < eth->vft_size; offset++)
		eth_wr32(eth, RNPGBE_VFTA(offset), 0);
}

static void rnpgbe_eth_set_doulbe_vlan(struct rnpgbe_eth_info *eth,
				       bool on)
{
	if (on)
		eth_wr32(eth, RNPGBE_ETH_VLAN_RM_TYPE, 1);
	else
		eth_wr32(eth, RNPGBE_ETH_VLAN_RM_TYPE, 0);
}

static void rnpgbe_eth_set_outer_vlan_type(struct rnpgbe_eth_info *eth,
					   int type)
{
	u32 data = 0x88a8;

	switch (type) {
	case outer_vlan_type_88a8:
		data = 0x88a8;
		break;
#ifdef ETH_P_QINQ1
	case outer_vlan_type_9100:
		data = 0x9100;
		break;
#endif /* ETH_P_QINQ1 */
#ifdef ETH_P_QINQ2
	case outer_vlan_type_9200:
		data = 0x9200;
		break;
#endif /* ETH_P_QINQ2 */
	}
	eth_wr32(eth, RNPGBE_ETH_WRAP_FIELD_TYPE, data);
	eth_wr32(eth, RNPGBE_ETH_TX_VLAN_TYPE, data);
}

/**
 *  rnpgbe_eth_set_vlan_filter - Set VLAN filter table
 *  @eth: pointer to eth structure
 *  @status: on |off
 *  Turn on/off VLAN filter table.
 **/
static void rnpgbe_eth_set_vlan_filter(struct rnpgbe_eth_info *eth,
				       bool status)
{
#define ETH_VLAN_FILTER_BIT (30)
	u32 value = eth_rd32(eth, RNPGBE_ETH_VLAN_FILTER_ENABLE);

	/* clear bit first */
	value &= (~(0x01 << ETH_VLAN_FILTER_BIT));
	if (status)
		value |= (0x01 << ETH_VLAN_FILTER_BIT);
	eth_wr32(eth, RNPGBE_ETH_VLAN_FILTER_ENABLE, value);
}

static u16 rnpgbe_layer2_pritologic(u16 hw_id)
{
	return hw_id;
}

static void rnpgbe_eth_set_layer2(struct rnpgbe_eth_info *eth,
				  union rnpgbe_atr_input *input,
				  u16 pri_id,
				  u8 queue, bool prio_flag)
{
	u16 hw_id;

	hw_id = rnpgbe_layer2_pritologic(pri_id);
	/* enable layer2 */
	eth_wr32(eth, RNPGBE_ETH_LAYER2_ETQF(hw_id),
		 (0x1 << 31) | (ntohs(input->layer2_formate.proto)));

	/* setup action */
	if (queue == RNP_FDIR_DROP_QUEUE) {
		eth_wr32(eth, RNPGBE_ETH_LAYER2_ETQS(hw_id), (0x1 << 31));
	} else {
		/* setup ring_number */
		if (prio_flag)
			eth_wr32(eth, RNPGBE_ETH_LAYER2_ETQS(hw_id),
				 (0x1 << 30) | (queue << 20) | (0x1 << 28));
		else
			eth_wr32(eth, RNPGBE_ETH_LAYER2_ETQS(hw_id),
				 (0x1 << 30) | (queue << 20));
	}
}

static void rnpgbe_eth_clr_layer2(struct rnpgbe_eth_info *eth, u16 pri_id)
{
	u16 hw_id;

	hw_id = rnpgbe_layer2_pritologic(pri_id);
	eth_wr32(eth, RNPGBE_ETH_LAYER2_ETQF(hw_id), 0);
}

static void rnpgbe_eth_clr_all_layer2(struct rnpgbe_eth_info *eth)
{
	int i;

	for (i = 0; i < RNPGBE_MAX_LAYER2_FILTERS; i++)
		eth_wr32(eth, RNPGBE_ETH_LAYER2_ETQF(i), 0);
}

static u16 rnpgbe_tuple5_pritologic(u16 hw_id)
{
	return hw_id;
}

static void rnpgbe_eth_set_tuple5(struct rnpgbe_eth_info *eth,
				  union rnpgbe_atr_input *input,
				  u16 pri_id,
				  u8 queue, bool prio_flag)
{
#define RNPGBE_SRC_IP_MASK BIT(0)
#define RNPGBE_DST_IP_MASK BIT(1)
#define RNPGBE_SRC_PORT_MASK BIT(2)
#define RNPGBE_DST_PORT_MASK BIT(3)
#define RNPGBE_L4_PROTO_MASK BIT(4)
	u8 l4_proto_type = 0;
	u8 mask_temp = 0;
	u32 port = 0;
	u16 hw_id;

	hw_id = rnpgbe_tuple5_pritologic(pri_id);
	if (input->formatted.src_ip[0] != 0) {
		eth_wr32(eth, RNPGBE_ETH_TUPLE5_SAQF(hw_id),
			 ntohl(input->formatted.src_ip[0]));
	} else {
		mask_temp |= RNPGBE_SRC_IP_MASK;
	}
	if (input->formatted.dst_ip[0] != 0) {
		eth_wr32(eth, RNPGBE_ETH_TUPLE5_DAQF(hw_id),
			 ntohl(input->formatted.dst_ip[0]));
	} else {
		mask_temp |= RNPGBE_DST_IP_MASK;
	}
	if (input->formatted.src_port != 0)
		port |= (ntohs(input->formatted.src_port));
	else
		mask_temp |= RNPGBE_SRC_PORT_MASK;
	if (input->formatted.dst_port != 0)
		port |= (ntohs(input->formatted.dst_port) << 16);
	else
		mask_temp |= RNPGBE_DST_PORT_MASK;

	if (port != 0)
		eth_wr32(eth, RNPGBE_ETH_TUPLE5_SDPQF(hw_id), port);

	switch (input->formatted.flow_type) {
	case RNP_ATR_FLOW_TYPE_TCPV4:
		l4_proto_type = IPPROTO_TCP;
		break;
	case RNP_ATR_FLOW_TYPE_UDPV4:
		l4_proto_type = IPPROTO_UDP;
		break;
	case RNP_ATR_FLOW_TYPE_SCTPV4:
		l4_proto_type = IPPROTO_SCTP;
		break;
	case RNP_ATR_FLOW_TYPE_IPV4:
		l4_proto_type = input->formatted.inner_mac[0];
		break;
	default:
		l4_proto_type = 0;
	}

	if (l4_proto_type == 0)
		mask_temp |= RNPGBE_L4_PROTO_MASK;

	/* setup ftqf */
	/* always set 0x3 */
	eth_wr32(eth, RNPGBE_ETH_TUPLE5_FTQF(hw_id),
		 (1 << 31) | (mask_temp << 25) | (l4_proto_type << 16) | 0x3);

	/* setup action */
	if (queue == RNP_FDIR_DROP_QUEUE) {
		eth_wr32(eth, RNPGBE_ETH_TUPLE5_POLICY(hw_id), (0x1 << 31));
	} else {
		/* setup ring_number */
		if (prio_flag)
			eth_wr32(eth, RNPGBE_ETH_TUPLE5_POLICY(hw_id),
				 ((0x1 << 30) | (queue << 20) | (0x1 << 28)));
		else
			eth_wr32(eth, RNPGBE_ETH_TUPLE5_POLICY(hw_id),
				 ((0x1 << 30) | (queue << 20)));
	}
}

static void rnpgbe_eth_clr_tuple5(struct rnpgbe_eth_info *eth, u16 pri_id)
{
	u16 hw_id;

	hw_id = rnpgbe_tuple5_pritologic(pri_id);
	eth_wr32(eth, RNPGBE_ETH_TUPLE5_FTQF(hw_id), 0);
}

static void rnpgbe_eth_clr_all_tuple5(struct rnpgbe_eth_info *eth)
{
	int i;

	for (i = 0; i < RNPGBE_MAX_TUPLE5_FILTERS; i++)
		eth_wr32(eth, RNPGBE_ETH_TUPLE5_FTQF(i), 0);
}

static void rnpgbe_eth_set_tcp_sync(struct rnpgbe_eth_info *eth,
				    int queue,
				    bool flag, bool prio)
{
	if (flag) {
		eth_wr32(eth, RNPGBE_ETH_SYNQF, (0x1 << 30) | (queue << 20));
		if (prio)
			eth_wr32(eth, RNPGBE_ETH_SYNQF_PRIORITY,
				 (0x1 << 31) | 0x1);
		else
			eth_wr32(eth, RNPGBE_ETH_SYNQF_PRIORITY, (0x1 << 31));
	} else {
		eth_wr32(eth, RNPGBE_ETH_SYNQF, 0);
		eth_wr32(eth, RNPGBE_ETH_SYNQF_PRIORITY, 0);
	}
}

static void rnpgbe_eth_set_rx_skip(struct rnpgbe_eth_info *eth,
				   int count,
				   bool flag)
{
	if (flag) {
		eth_wr32(eth, RNPGBE_ETH_PRIV_DATA_CONTROL_REG,
			 PRIV_DATA_EN | count);
	} else {
		eth_wr32(eth, RNPGBE_ETH_PRIV_DATA_CONTROL_REG, 0);
	}
}

static void rnpgbe_eth_set_min_max_packets(struct rnpgbe_eth_info *eth,
					   int min, int max)
{
	eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_MIN_LEN, min);
	eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_MAX_LEN, max);
}

static void rnpgbe_eth_set_vlan_strip(struct rnpgbe_eth_info *eth,
				      u16 queue, bool enable)
{
	u32 reg = RNPGBE_ETH_VLAN_VME_REG(queue / 32);
	u32 data = eth_rd32(eth, reg);
	u32 offset = queue % 32;

	if (enable == true)
		data |= (1 << offset);
	else
		data &= ~(1 << offset);

	eth_wr32(eth, reg, data);
}

static void rnpgbe_eth_set_rx_hash(struct rnpgbe_eth_info *eth,
					bool status, bool sriov_flag)
{
	u32 iov_en = (sriov_flag) ? RNPGBE_IOV_ENABLED : 0;
	u32 data;

	data = eth_rd32(eth, RNPGBE_ETH_RSS_CONTROL);
	data &= ~RNPGBE_ETH_RSS_MASK;

	if (status) {
		data |= RNPGBE_ETH_ENABLE_RSS_ONLY;
		eth_wr32(eth, RNPGBE_ETH_RSS_CONTROL, data | iov_en);
	} else {
		eth_wr32(eth, RNPGBE_ETH_RSS_CONTROL, data | iov_en);
	}
}

static void rnpgbe_eth_set_rx(struct rnpgbe_eth_info *eth, bool status)
{
	if (status) {
		eth_wr32(eth, RNPGBE_ETH_EXCEPT_DROP_PROC, 0);
		eth_wr32(eth, RNPGBE_ETH_TX_MUX_DROP, 0);
	} else {
		eth_wr32(eth, RNPGBE_ETH_EXCEPT_DROP_PROC, 1);
		eth_wr32(eth, RNPGBE_ETH_TX_MUX_DROP, 1);
	}
}

static void rnpgbe_eth_fcs(struct rnpgbe_eth_info *eth, bool status)
{
	if (status)
		eth_wr32(eth, RNPGBE_ETH_FCS_EN, 1);
	else
		eth_wr32(eth, RNPGBE_ETH_FCS_EN, 0);
}

static void rnpgbe_eth_set_vf_vlan_mode(struct rnpgbe_eth_info *eth,
					u16 vlan, int vf, bool enable)
{
	u32 value = vlan;

	if (enable)
		value |= BIT(31);

	eth_wr32(eth, RNPGBE_VLVF(vf), value);
	eth_wr32(eth, RNPGBE_VLVF_TABLE(vf), vf);
}

static s32 rnpgbe_eth_set_fc_mode(struct rnpgbe_eth_info *eth)
{
	struct rnpgbe_hw *hw = (struct rnpgbe_hw *)eth->back;
	s32 ret_val = 0;
	int i;
	/* rnpgbe has only 1 traffic class */
	for (i = 0; i < 1; i++) {
		if ((hw->fc.current_mode & rnpgbe_fc_tx_pause) &&
		    hw->fc.high_water[i]) {
			if (!hw->fc.low_water[i] ||
			    hw->fc.low_water[i] >= hw->fc.high_water[i]) {
				hw_dbg(hw,
				       "Invalid water mark configuration\n");
				ret_val = RNP_ERR_INVALID_LINK_SETTINGS;
				goto out;
			}
		}
	}

	for (i = 0; i < 1; i++) {
		if ((hw->fc.current_mode & rnpgbe_fc_tx_pause)) {
			if (hw->fc.high_water[i]) {
				eth_wr32(eth, RNPGBE_ETH_HIGH_WATER(i),
					 hw->fc.high_water[i]);
			}
			if (hw->fc.low_water[i]) {
				eth_wr32(eth, RNPGBE_ETH_LOW_WATER(i),
					 hw->fc.low_water[i]);
			}
		}
	}
out:
	return ret_val;
}

static struct rnpgbe_eth_operations eth_ops_rnpgbe = {
	.set_rar = &rnpgbe_eth_set_rar,
	.clear_rar = &rnpgbe_eth_clear_rar,
	.set_vmdq = &rnpgbe_eth_set_vmdq,
	.clear_vmdq = &rnpgbe_eth_clear_vmdq,
	.update_mc_addr_list = &rnpgbe_eth_update_mc_addr_list,
	.clr_mc_addr = &rnpgbe_eth_clr_mc_addr,
	/* store rss info to eth */
	.set_rss_hfunc = &rnpgbe_eth_set_rss_hfunc,
	.set_rss_key = &rnpgbe_eth_update_rss_key,
	.set_rss_table = &rnpgbe_eth_update_rss_table,
	.set_vfta = &rnpgbe_eth_set_vfta,
	.clr_vfta = &rnpgbe_eth_clr_vfta,
	.set_vlan_filter = &rnpgbe_eth_set_vlan_filter,
	.set_outer_vlan_type = &rnpgbe_eth_set_outer_vlan_type,
	.set_double_vlan = &rnpgbe_eth_set_doulbe_vlan,
	.set_layer2_remapping = &rnpgbe_eth_set_layer2,
	.clr_layer2_remapping = &rnpgbe_eth_clr_layer2,
	.clr_all_layer2_remapping = &rnpgbe_eth_clr_all_layer2,
	.set_tuple5_remapping = &rnpgbe_eth_set_tuple5,
	.clr_tuple5_remapping = &rnpgbe_eth_clr_tuple5,
	.clr_all_tuple5_remapping = &rnpgbe_eth_clr_all_tuple5,
	.set_tcp_sync_remapping = &rnpgbe_eth_set_tcp_sync,
	.set_rx_skip = &rnpgbe_eth_set_rx_skip,
	.set_min_max_packet = &rnpgbe_eth_set_min_max_packets,
	.set_vlan_strip = &rnpgbe_eth_set_vlan_strip,
	.set_rx_hash = &rnpgbe_eth_set_rx_hash,
	.set_fc_mode = &rnpgbe_eth_set_fc_mode,
	.set_rx = &rnpgbe_eth_set_rx,
	.set_fcs = &rnpgbe_eth_fcs,
	.set_vf_vlan_mode = &rnpgbe_eth_set_vf_vlan_mode,
};

/**
 *  rnpgbe_init_hw_ops - Generic hardware initialization
 *  @hw: pointer to hardware structure
 *
 *  Initialize the hardware by resetting the hardware, filling the bus info
 *  structure and media type, clears all on chip counters, initializes receive
 *  address registers, multicast table, VLAN filter table, calls routine to set
 *  up link and flow control settings, and leaves transmit and receive units
 *  disabled and uninitialized
 **/
static s32 rnpgbe_init_hw_ops(struct rnpgbe_hw *hw)
{
	s32 status = 0;

	/* Reset the hardware */
	status = hw->ops.reset_hw(hw);

	if (status == 0) {
		/* Start the HW */
		status = hw->ops.start_hw(hw);
	}

	return status;
}

static s32 rnpgbe_get_permtion_mac_addr(struct rnpgbe_hw *hw,
					u8 *mac_addr)
{
	struct device *dev = &hw->pdev->dev;

	if (rnpgbe_fw_get_macaddr(hw, hw->pfvfnum, mac_addr, hw->nr_lane)) {
		dev_info(dev, "generate random macaddress...\n");
		eth_random_addr(mac_addr);
	} else {
		/*dev_info(dev, "get mac addr %x:%x:%x:%x:%x:%x\n",
			 mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
			 mac_addr[4], mac_addr[5]);
		*/
		if (!is_valid_ether_addr(mac_addr))
			eth_random_addr(mac_addr);
	}
	hw->mac.mac_flags |= RNP_FLAGS_INIT_MAC_ADDRESS;
	return 0;
}

static s32 rnpgbe_reset_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct rnpgbe_eth_info *eth = &hw->eth;
	int i;

	/* Call adapter stop to disable tx/rx and clear interrupts */
	dma_wr32(dma, RNP_DMA_AXI_EN, 0);
	rnpgbe_mbx_fw_reset_phy(hw);
	/* tcam not reset */
	eth->ops.clr_all_tuple5_remapping(eth);
	/* Store the permanent mac address */
	if (!(hw->mac.mac_flags & RNP_FLAGS_INIT_MAC_ADDRESS)) {
		rnpgbe_get_permtion_mac_addr(hw, hw->mac.perm_addr);
		memcpy(hw->mac.addr, hw->mac.perm_addr, ETH_ALEN);
	}

	hw->ops.init_rx_addrs(hw);
	/* default drop */
	eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR, 0);
	hw_wr32(hw, RNP_DMA_RX_DATA_PROG_FULL_THRESH, 0xa);

	for (i = 0; i < 12; i++)
		rnpgbe_wr_reg(hw->ring_msix_base + RING_VECTOR(i), 0);

	if (hw->ncsi_en)
		rnpgbe_mbx_phy_pause_get(hw, &hw->fc.requested_mode);
	else
		rnpgbe_mbx_phy_pause_set(hw, hw->fc.requested_mode);

	rnpgbe_mbx_get_lane_stat(hw);
	hw->link = 0;

	return 0;
}

static s32 rnpgbe_start_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_dma_info *dma = &hw->dma;
	s32 ret_val = 0;

	eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR, 0);
	eth_wr32(eth, RNPGBE_ETH_BYPASS, 0);
	eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_RING, 0);
	dma_wr32(dma, RNP_DMA_CONFIG, DMA_VEB_BYPASS);
	dma_wr32(dma, RNP_DMA_AXI_EN, (RX_AXI_RW_EN | TX_AXI_RW_EN));

	{
		int value = dma_rd32(dma, RNP_DMA_DUMY);

		value |= RC_CONTROL_HW;
		dma_wr32(dma, RNP_DMA_DUMY, value);
	}
	return ret_val;
}

/* set rnpgbe min/max packet according to new_mtu
 * we support mtu + 14 + 4 * 3 as max packet LENGTH_ERROR
 */
static void rnpgbe_set_mtu_hw_ops(struct rnpgbe_hw *hw, int new_mtu)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_eth_info *eth = &hw->eth;

	int min;
	int max = new_mtu + ETH_HLEN + ETH_FCS_LEN * 2;
#define ULTRA_SHORT 33
#define DEFAULT_SHORT 60
	if ((adapter->priv_flags & RNP_PRIV_FLAG_ULTRA_SHORT) ||
	    (adapter->priv_flags & RNP_PRIV_FLAG_RX_ALL))
		min = ULTRA_SHORT;
	else
		min = DEFAULT_SHORT;

	/* we receive jumbo frame only in jumbo enable or rx all mode */
	if ((adapter->priv_flags & RNP_PRIV_FLAG_JUMBO) ||
	    (adapter->priv_flags & RNP_PRIV_FLAG_RX_ALL))
		max = hw->max_length;
	/* if ncsi not set max too small */
	if ((hw->ncsi_en) && (max < 1522))
		max = 1522;

	hw->min_length_current = min;
	hw->max_length_current = max;
	eth->ops.set_min_max_packet(eth, min, max);
}

static void rnpgbe_set_vlan_filter_en_hw_ops(struct rnpgbe_hw *hw,
						  bool status)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_vlan_filter(eth, status);
}

static void rnpgbe_set_vlan_filter_hw_ops(struct rnpgbe_hw *hw, u16 vid,
					  bool enable, bool sriov_flag)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_dma_info *dma = &hw->dma;
	u16 ncsi_vid;
	int ret;
	int i;

	//todo set up own veb , use the last vfnum
	u32 vfnum = hw->max_vfs - 1;
	/* setup rnpgbe eth vlan table */
	eth->ops.set_vfta(eth, vid, enable);

	/* setup veb */
	if (sriov_flag) {
		if (hw->feature_flags & RNP_VEB_VLAN_MASK_EN) {
			/* we update veb int other location */
		} else {
			if (enable)
				dma->ops.set_veb_vlan(dma, vid, vfnum);
			else
				dma->ops.set_veb_vlan(dma, 0, vfnum);
		}
	}
	/* always setup nsci vid */
	for (i = 0; i < 2; i++) {
		ret = hw->ops.get_ncsi_vlan(hw, &ncsi_vid, i);
		if (!ret) {
			eth->ops.set_vfta(eth, ncsi_vid, 1);
			//printk(KERN_DEBUG "update ncsi vid %d\n", ncsi_vid);
		}
	}
}

static int rnpgbe_set_veb_vlan_mask_hw_ops(struct rnpgbe_hw *hw, u16 vid,
					   int vf, bool enable)
{
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct vf_vebvlans *entry;
	struct list_head *pos;
	bool find = false;
	int err = 0;
	/* 1 try to find is this vid is in vlan mask table */
	list_for_each(pos, &hw->vf_vas.l) {
		entry = list_entry(pos, struct vf_vebvlans, l);
		if (entry->vid == vid) {
			find = true;
			break;
		}
	}
	if (find) {
		/* this vid is used before */
		if (enable) {
			entry->mask |= (1 << vf);
		} else {
			entry->mask &= (~(1 << vf));
			/* if mask is zero free this */
			if (!entry) {
				entry->vid = -1;
				entry->free = true;
			}
		}
	} else {
		/* 2 try to get new entries */
		list_for_each(pos, &hw->vf_vas.l) {
			entry = list_entry(pos, struct vf_vebvlans, l);
			if (entry->free == true) {
				find = true;
				break;
			}
		}
		if (find) {
			entry->free = false;
			entry->vid = vid;
			entry->mask |= (1 << vf);
		} else {
			err = -1;
			goto err_out;
		}
	}
	/* 3 update new vlan mask to hw */
	dma->ops.set_veb_vlan_mask(dma, entry->vid, entry->mask,
				   entry->veb_entry);
err_out:
	return err;
}

static void rnpgbe_set_vf_vlan_filter_hw_ops(struct rnpgbe_hw *hw, u16 vid,
					     int vf, bool enable,
					     bool veb_only)
{
	struct rnpgbe_dma_info *dma = &hw->dma;

	if (!veb_only) {
		/* call set vfta without veb setup */
		hw->ops.set_vlan_filter(hw, vid, enable, false);

	} else {
		if (enable)
			dma->ops.set_veb_vlan(dma, vid, vf);
		else
			dma->ops.set_veb_vlan(dma, 0, vf);
	}
}

static void rnpgbe_clr_vlan_veb_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_dma_info *dma = &hw->dma;
	u32 vfnum = hw->vfnum;

	dma->ops.set_veb_vlan(dma, 0, vfnum);
}

static void rnpgbe_set_vlan_strip_hw_ops(struct rnpgbe_hw *hw, u16 queue,
					      bool strip)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_vlan_strip(eth, queue, strip);
}

static void rnpgbe_set_mac_hw_ops(struct rnpgbe_hw *hw, u8 *mac,
				  bool sriov_flag)
{
	struct rnpgbe_mac_info *mac_info = &hw->mac;
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_dma_info *dma = &hw->dma;
	/* use this queue index to setup veb */
	/* now pf use queue 0 /1
	 * vfnum is the last vfnum
	 */
	int queue = hw->veb_ring;
	int vfnum = hw->vfnum;

	eth->ops.set_rar(eth, 0, mac, true);
	if (sriov_flag) {
		eth->ops.set_vmdq(eth, 0, queue / hw->sriov_ring_limit);
		dma->ops.set_veb_mac(dma, mac, vfnum, queue);
	}

	mac_info->ops.set_mac(mac_info, mac, 0);
}

/**
 * rnpgbe_write_uc_addr_list - write unicast addresses to RAR table
 * @hw: hw structure
 * @netdev: network interface device structure
 * @sriov_flag: sriov flag
 *
 * Writes unicast address list to the RAR table.
 * Returns: -ENOMEM on failure/insufficient address space
 *                0 on no addresses written
 *                X on writing X addresses to the RAR table
 **/
static int rnpgbe_write_uc_addr_list(struct rnpgbe_hw *hw,
				     struct net_device *netdev,
				     bool sriov_flag)
{
	unsigned int rar_entries = hw->num_rar_entries - 1;
	struct rnpgbe_eth_info *eth = &hw->eth;
	u32 vfnum = hw->vfnum;
	u8 ncsi_addr[6];
	int count = 0;
	int i = 0;
	int ret;

	/* In SR-IOV mode significantly less RAR entries are available */
	if (sriov_flag)
		rar_entries = hw->max_pf_macvlans - 1;

	/* return ENOMEM indicating insufficient memory for addresses */
	if (netdev_uc_count(netdev) > rar_entries)
		return -ENOMEM;

	if (!netdev_uc_empty(netdev)) {
		struct netdev_hw_addr *ha;

		hw_dbg(hw, "%s: rar_entries:%d, uc_count:%d\n", __func__,
		       hw->num_rar_entries, netdev_uc_count(netdev));

		/* return error if we do not support writing to RAR table */
		if (!eth->ops.set_rar)
			return -ENOMEM;

		netdev_for_each_uc_addr(ha, netdev) {
			if (!rar_entries)
				break;
			eth->ops.set_rar(eth, rar_entries, ha->addr,
					 RNPGBE_RAH_AV);
			if (sriov_flag)
				eth->ops.set_vmdq(eth, rar_entries, vfnum);

			rar_entries--;

			count++;
		}
	}
	for (i = 0; i < NCSI_RAR_NUM; i++) {
		ret = hw->ops.get_ncsi_mac(hw, ncsi_addr, i);
		if (!ret) {
			eth->ops.set_rar(eth, NCSI_RAR_IDX_START + i, ncsi_addr,
					 RNPGBE_RAH_AV);
		}
	}

	/* write the addresses in reverse order to avoid write combining */
	hw_dbg(hw, "%s: Clearing RAR[1 - %d]\n", __func__, rar_entries);
	for (; rar_entries > 0; rar_entries--)
		eth->ops.clear_rar(eth, rar_entries);

	return count;
}

static void rnpgbe_set_rx_mode_hw_ops(struct rnpgbe_hw *hw,
				      struct net_device *netdev,
				      bool sriov_flag)
{
#if defined(NETIF_F_HW_VLAN_CTAG_FILTER) || defined(NETIF_F_HW_VLAN_CTAG_RX)
	netdev_features_t features = netdev->features;
#endif
	struct rnpgbe_eth_info *eth = &hw->eth;
	int count;
	u32 fctrl;

	/* broadcast always bypass */
	fctrl = eth_rd32(eth, RNPGBE_ETH_DMAC_FCTRL) | RNPGBE_FCTRL_BPE;

	/* clear the bits we are changing the status of */
	fctrl &= ~(RNPGBE_FCTRL_UPE | RNPGBE_FCTRL_MPE);
	/* promisc mode */
	if (netdev->flags & IFF_PROMISC) {
		hw->addr_ctrl.user_set_promisc = true;
		fctrl |= (RNPGBE_FCTRL_UPE | RNPGBE_FCTRL_MPE);
		/* disable hardware filter vlans in promisc mode */
#ifdef NETIF_F_HW_VLAN_CTAG_FILTER
		features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
#endif /* NETIF_F_HW_VLAN_CTAG_FILTER */
#ifdef NETIF_F_HW_VLAN_CTAG_RX
		features &= ~NETIF_F_HW_VLAN_CTAG_RX;
#endif /* NETIF_F_HW_VLAN_CTAG_RX */
	} else {
		if (netdev->flags & IFF_ALLMULTI) {
			fctrl |= RNPGBE_FCTRL_MPE;
		} else {
			/* Write addresses to the MTA, if the attempt fails
			 * then we should just turn on promiscuous mode so
			 * that we can at least receive multicast traffic
			 */
			count = eth->ops.update_mc_addr_list(eth, netdev, true);
			if (count < 0)
				fctrl |= RNPGBE_FCTRL_MPE;
		}
		hw->addr_ctrl.user_set_promisc = false;
	}

	/*
	 * Write addresses to available RAR registers, if there is not
	 * sufficient space to store all the addresses then enable
	 * unicast promiscuous mode
	 */
	if (rnpgbe_write_uc_addr_list(hw, netdev, sriov_flag) < 0)
		fctrl |= RNPGBE_FCTRL_UPE;

	eth_wr32(eth, RNPGBE_ETH_DMAC_FCTRL, fctrl);
#ifdef NETIF_F_HW_VLAN_CTAG_FILTER
	if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
		eth->ops.set_vlan_filter(eth, true);
	else
		eth->ops.set_vlan_filter(eth, false);
#endif /* NETIF_F_HW_VLAN_CTAG_FILTER */

	hw->ops.set_mtu(hw, netdev->mtu);
}

/* setup an rar with vfnum */
static void rnpgbe_set_rar_with_vf_hw_ops(struct rnpgbe_hw *hw, u8 *mac,
					  int idx, u32 vfnum, bool enable)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_rar(eth, idx, mac, enable);
	/* should check error or not ?*/
	eth->ops.set_vmdq(eth, idx, vfnum);
}

static void rnpgbe_clr_rar_hw_ops(struct rnpgbe_hw *hw, int idx)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.clear_rar(eth, idx);
}

static void rnpgbe_clr_rar_all_hw_ops(struct rnpgbe_hw *hw)
{
	unsigned int rar_entries = hw->num_rar_entries - 1;
	struct rnpgbe_eth_info *eth = &hw->eth;
	int i;

	for (i = 0; i < rar_entries; i++)
		eth->ops.clear_rar(eth, rar_entries);
}

static void rnpgbe_set_fcs_mode_hw_ops(struct rnpgbe_hw *hw, bool status)
{
	struct rnpgbe_mac_info *mac = &hw->mac;
	struct rnpgbe_eth_info *eth = &hw->eth;

	mac->ops.set_mac_fcs(mac, status);
	eth->ops.set_fcs(eth, status);
}

static void rnpgbe_set_mac_speed_hw_ops(struct rnpgbe_hw *hw, bool link,
					u32 speed, bool duplex)
{
	/* rnpgbe hw control this */
}

static void rnpgbe_set_mac_rx_hw_ops(struct rnpgbe_hw *hw, bool status)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_mac_info *mac = &hw->mac;

	if (pci_device_check_offline(hw->pdev))
		return;

	if (status) {
		mac->ops.set_mac_rx(mac, status);
		eth->ops.set_rx(eth, status);
	} else {
		eth->ops.set_rx(eth, status);
		mac->ops.set_mac_rx(mac, status);
	}
}

static void rnpgbe_set_sriov_status_hw_ops(struct rnpgbe_hw *hw,
					   bool status)
{
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct rnpgbe_eth_info *eth = &hw->eth;
	u32 v, fctrl;

	fctrl = eth_rd32(eth, RNPGBE_ETH_DMAC_FCTRL);
#define RNPGBE_DMAC_MASK (0x7f)
	fctrl &= ~RNPGBE_DMAC_MASK;

	if (status) {
		fctrl |= hw->veb_ring;
		eth_wr32(eth, RNPGBE_ETH_DMAC_FCTRL, fctrl);
		/* setup default ring */
		dma_wr32(dma, RNP_DMA_CONFIG,
			 dma_rd32(dma, RNP_DMA_CONFIG) & (~DMA_VEB_BYPASS));
		v = eth_rd32(eth, RNPGBE_MRQC_IOV_EN);
		v |= RNPGBE_IOV_ENABLED;
		eth_wr32(eth, RNPGBE_MRQC_IOV_EN, v);
		/* 1 setup veb vlan type */

	} else {
		eth_wr32(eth, RNPGBE_ETH_DMAC_FCTRL, fctrl);
		v = eth_rd32(eth, RNPGBE_MRQC_IOV_EN);
		v &= ~(RNPGBE_IOV_ENABLED);
		eth_wr32(eth, RNPGBE_MRQC_IOV_EN, v);
		dma->ops.clr_veb_all(dma);
	}
}

static void rnpgbe_set_sriov_vf_mc_hw_ops(struct rnpgbe_hw *hw,
					  u16 mc_addr)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	u32 vector_bit;
	u32 vector_reg;
	u32 mta_reg;

	vector_reg = (mc_addr >> 5) & 0x7F;
	vector_bit = mc_addr & 0x1F;
	mta_reg = eth_rd32(eth, RNPGBE_ETH_MUTICAST_HASH_TABLE(vector_reg));
	mta_reg |= (1 << vector_bit);
	eth_wr32(eth, RNPGBE_ETH_MUTICAST_HASH_TABLE(vector_reg), mta_reg);
}

static void rnpgbe_update_sriov_info_hw_ops(struct rnpgbe_hw *hw)
{
	/* update sriov info to hw */
}

static void rnpgbe_set_pause_mode_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_mac_info *mac = &hw->mac;
	struct rnpgbe_eth_info *eth = &hw->eth;

	mac->ops.set_fc_mode(mac);
	eth->ops.set_fc_mode(eth);
}

static void rnpgbe_get_pause_mode_hw_ops(struct rnpgbe_hw *hw)
{
	/* rnpgbe can get pause mode in link event */
}

static void rnpgbe_update_hw_info_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_mac_info *mac = &hw->mac;
	u32 data;
	/* 1 enable eth filter */
	eth_wr32(eth, RNPGBE_HOST_FILTER_EN, 1);
	/* 2 open redir en */
	eth_wr32(eth, RNPGBE_REDIR_EN, 1);
	/* 3 open sctp checksum and other checksum */
	if (hw->feature_flags & RNP_NET_FEATURE_TX_CHECKSUM)
		eth_wr32(eth, RNPGBE_ETH_SCTP_CHECKSUM_EN, 1);
	/* 4 mark muticaset as broadcast */
	dma_wr32(dma, RNPGBE_VEB_MAC_MASK_LO, 0xffffffff);
	dma_wr32(dma, RNPGBE_VEB_MAC_MASK_HI, 0xfeff);
	/* 5 setup ft padding and veb vlan mode */
	data = dma_rd32(dma, RNP_DMA_CONFIG);
#ifdef FT_PADDING
#define N500_PADDING_BIT 8
	if (adapter->priv_flags & RNP_PRIV_FLAG_FT_PADDING)
		SET_BIT(N500_PADDING_BIT, data);
#endif
	/* force close padding in rnpgbe */
	CLR_BIT(8, data);

#define N500_VLAN_POLL_EN BIT(3)
	if (hw->feature_flags & RNP_VEB_VLAN_MASK_EN)
		data |= N500_VLAN_POLL_EN;

	dma_wr32(dma, RNP_DMA_CONFIG, data);
	/* 6 setup vlan mode */
	if (adapter->priv_flags & RNP_PRIV_FLAG_DOUBLE_VLAN)
		eth->ops.set_double_vlan(eth, true);
	else
		eth->ops.set_double_vlan(eth, false);
	/* 7 setup rss-hash mode */
	eth->ops.set_rss_hfunc(eth, adapter->rss_func_mode);
	/* 8 setup outer-vlan type */
	eth->ops.set_outer_vlan_type(eth, adapter->outer_vlan_type);
	/* 9 setup tcp sync remapping */
	if (adapter->priv_flags & RNP_PRIV_FLAG_TCP_SYNC) {
		if (adapter->priv_flags & RNP_PRIV_FLAG_TCP_SYNC_PRIO)
			hw->ops.set_tcp_sync_remapping(
				hw, adapter->tcp_sync_queue, true, true);
		else
			hw->ops.set_tcp_sync_remapping(
				hw, adapter->tcp_sync_queue, true, false);
	} else {
		hw->ops.set_tcp_sync_remapping(hw, adapter->tcp_sync_queue,
					       false, false);
	}
	/* 10 setup pause status */
	data = mac_rd32(mac, GMAC_FLOW_CTRL);
	if (adapter->priv_flags & RNP_PRIV_FLAG_PAUSE_OWN)
		data |= GMAC_FLOW_CTRL_UP;
	else
		data &= (~GMAC_FLOW_CTRL_UP);
	mac_wr32(mac, GMAC_FLOW_CTRL, data);
	/* 11 open tx double vlan according to stags */
	eth_wr32(eth, RNPGBE_ETH_TX_VLAN_CONTROL_EANBLE, 1);
	/* 12 test */
	eth_wr32(eth, RNPGBE_ETH_WHOLE_PKT_LEN_ERR_DROP, 1);

	/* 13 setup double vlan drop */
	if (adapter->priv_flags & RNP_PRIV_FLAG_DOUBLE_VLAN_RECEIVE)
		eth_wr32(eth, RNPGBE_ETH_DOUBLE_VLAN_DROP, 0);
	else
		eth_wr32(eth, RNPGBE_ETH_DOUBLE_VLAN_DROP, 1);

	/* 14 open error mask if in rx all mode */
	if (adapter->priv_flags & RNP_PRIV_FLAG_RX_ALL) {
		eth_wr32(eth, RNPGBE_MAC_ERR_MASK,
			 RUN_FRAME_ERROR | GAINT_FRAME_ERROR | CRC_ERROR |
				 LENGTH_ERROR);
		/* we open this in rx all mode */
		eth_wr32(eth, RNPGBE_ETH_DOUBLE_VLAN_DROP, 0);
#define FORWARD_ALL_CONTROL (0x2)
		eth_wr32(eth, RNPGBE_BAD_PACKETS_RECEIVE_EN, 1);
		mac_wr32(mac, GMAC_FRAME_FILTER,
			 0x00000001 | (FORWARD_ALL_CONTROL << 6));
	} else {
		eth_wr32(eth, RNPGBE_MAC_ERR_MASK,
			 RUN_FRAME_ERROR | GAINT_FRAME_ERROR);
		eth_wr32(eth, RNPGBE_BAD_PACKETS_RECEIVE_EN, 0);
		mac_wr32(mac, GMAC_FRAME_FILTER, 0x00000001);
	}
	/* setup eth_err_mask */
	if ((adapter->priv_flags & RNP_PRIV_FLAG_RX_ALL) ||
	    (adapter->priv_flags & RNP_PRIV_FLAG_REC_HDR_LEN_ERR)) {
		/* set pkt_len_err and hdr_len_err default to 1 */
		eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR,
			 PKT_LEN_ERR | HDR_LEN_ERR);
	} else {
		/* set 0 to drop in default */
		eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR, 0);
	}

	/* 15 update water according to max length */
	{
#define FIFO_ALL (1024)
		int water_high =
			FIFO_ALL - ((hw->max_length_current + 15) >> 4);
		/* rnpgbe only use one */
		hw->fc.high_water[0] = water_high;
		hw->fc.low_water[0] = water_high;

		dma_wr32(dma, RNPGBE_DMA_RBUF_FIFO,
			 ((hw->max_length_current + 15) >> 4) + 5);

		eth_wr32(eth, RNPGBE_ETH_EMAC_PARSE_PROGFULL_THRESH,
			 ((hw->max_length_current + 15) >> 4) + 2);
	}
	/* 16 setup fcs mode */
	if (adapter->priv_flags & RNP_PRIV_FLAG_RX_FCS)
		hw->ops.set_fcs_mode(hw, true);
	else
		hw->ops.set_fcs_mode(hw, false);

	/* 17 setup tso fifo */
	dma_wr32(dma, RNP_DMA_PKT_FIFO_DATA_PROG_FULL_THRESH, 36);
	/* 18 setup priv skip */
	if (adapter->priv_flags & RNP_PRIV_FLAG_RX_SKIP_EN)
		data = PRIV_DATA_EN | adapter->priv_skip_count;
	else
		data = 0;
	eth_wr32(eth, RNPGBE_ETH_PRIV_DATA_CONTROL_REG, data);
	/* 19 setup mac count read self clear */
	data = mac_rd32(mac, GMAC_COUNT_CONTROL);
#define READ_CLEAR BIT(2)
	data |= READ_CLEAR;
	mac_wr32(mac, GMAC_COUNT_CONTROL, data);
	/* 20 setup prio */
	if (adapter->priv_flags &
	    (RNP_PRIV_FLAG_8023_PRIO | RNP_PRIV_FLAG_REMAP_PRIO)) {
		eth_wr32(eth, RNPGBE_PRIORITY_1_MARK, RNPGBE_PRIORITY_1);
		eth_wr32(eth, RNPGBE_PRIORITY_0_MARK, RNPGBE_PRIORITY_0);
		eth_wr32(eth, RNPGBE_PRIORITY_EN, 1);
		if (adapter->priv_flags & RNP_PRIV_FLAG_8023_PRIO)
			eth_wr32(eth, RNPGBE_PRIORITY_EN_8023, 1);
		else
			eth_wr32(eth, RNPGBE_PRIORITY_EN_8023, 0);
	} else {
		eth_wr32(eth, RNPGBE_PRIORITY_EN, 0);
	}
}

static void rnpgbe_update_hw_rx_drop_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_ring *ring;
	int i;

	for (i = 0; i < adapter->num_rx_queues; i++) {
		ring = adapter->rx_ring[i];
		if (adapter->rx_drop_status & BIT(i)) {
			ring_wr32(ring, PCI_DMA_REG_RX_DESC_TIMEOUT_TH,
				  adapter->drop_time);
		} else {
			ring_wr32(ring, PCI_DMA_REG_RX_DESC_TIMEOUT_TH, 0);
		}
	}
}

static void rnpgbe_set_rx_hash_hw_ops(struct rnpgbe_hw *hw, bool status,
				      bool sriov_flag)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_rx_hash(eth, status, sriov_flag);
}

/*
 * setup mac to rar 0
 * clean vmdq
 * clean mc addr
 */
static s32 rnpgbe_init_rx_addrs_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	u32 rar_entries = eth->num_rar_entries;
	u32 i;
	u32 v;

	hw_dbg(hw, "init_rx_addrs:rar_entries:%d, mac.addr:%pM\n", rar_entries,
	       hw->mac.addr);
	/*
	 * If the current mac address is valid, assume it is a software override
	 * to the permanent address.
	 * Otherwise, use the permanent address from the eeprom.
	 */
	if (!is_valid_ether_addr(hw->mac.addr)) {
		/* Get the MAC address from the RAR0 for later reference */
		memcpy(hw->mac.addr, hw->mac.perm_addr, ETH_ALEN);
		hw_dbg(hw, " Keeping Current RAR0 Addr =%pM\n", hw->mac.addr);
	} else {
		/* Setup the receive address. */
		hw_dbg(hw, "Overriding MAC Address in RAR[0]\n");
		hw_dbg(hw, " New MAC Addr =%pM\n", hw->mac.addr);

		eth->ops.set_rar(eth, 0, hw->mac.addr, true);
		/*  clear VMDq pool/queue selection for RAR 0 */
		eth->ops.clear_vmdq(eth, 0, RNP_CLEAR_VMDQ_ALL);
	}
	hw->addr_ctrl.overflow_promisc = 0;
	hw->addr_ctrl.rar_used_count = 1;

	/* Zero out the other receive addresses. */
	hw_dbg(hw, "Clearing RAR[1-%d]\n", rar_entries - 1);
	if (!hw->ncsi_en) {
		for (i = 1; i < rar_entries; i++)
			eth->ops.clear_rar(eth, i);
	}

	/* Clear the MTA */
	hw->addr_ctrl.mta_in_use = 0;
	v = eth_rd32(eth, RNPGBE_ETH_DMAC_MCSTCTRL);
	v &= (~0x3);
	v |= eth->mc_filter_type;
	eth_wr32(eth, RNPGBE_ETH_DMAC_MCSTCTRL, v);

	hw_dbg(hw, " Clearing MTA\n");
	if (!hw->ncsi_en)
		eth->ops.clr_mc_addr(eth);

	return 0;
}

static void rnpgbe_clr_vfta_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.clr_vfta(eth);
}

static void rnpgbe_set_txvlan_mode_hw_ops(struct rnpgbe_hw *hw, bool cvlan)
{
	/* rnpgbe not support this */
}

static int rnpgbe_set_rss_hfunc_hw_ops(struct rnpgbe_hw *hw, u8 hfunc)
{
#ifdef HAVE_RXFH_HASHFUNC
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_eth_info *eth = &hw->eth;

	switch (hfunc) {
	case ETH_RSS_HASH_TOP:
		adapter->rss_func_mode = rss_func_top;
		break;

	case ETH_RSS_HASH_XOR:
		adapter->rss_func_mode = rss_func_xor;
		break;
	default:
		return -EINVAL;
	}

	eth->ops.set_rss_hfunc(eth, adapter->rss_func_mode);
#endif /* HAVE_RXFH_HASHFUNC */
	return 0;
}

static void rnpgbe_set_rss_key_hw_ops(struct rnpgbe_hw *hw,
				      bool sriov_flag)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_eth_info *eth = &hw->eth;
	int key_len = RNP_RSS_KEY_SIZE;

	memcpy(hw->rss_key, adapter->rss_key, key_len);

	eth->ops.set_rss_key(eth, sriov_flag);
}

static void rnpgbe_set_rss_table_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_rss_table(eth);
}

static void rnpgbe_set_mbx_link_event_hw_ops(struct rnpgbe_hw *hw,
					     int enable)
{
	rnpgbe_mbx_link_event_enable(hw, enable);
}

static void rnpgbe_set_mbx_ifup_hw_ops(struct rnpgbe_hw *hw, int enable)
{
	rnpgbe_mbx_ifup_down(hw, enable);
}

/**
 *  rnpgbe_check_mac_link_hw_ops - Determine link and speed status
 *  @hw: pointer to hardware structure
 *  @speed: pointer to link speed
 *  @link_up: true when link is up
 *  @duplex: full or half
 *  @link_up_wait_to_complete: bool used to wait for link up or not
 *
 *  Reads the links register to determine if link is up and the current speed
 **/
static s32 rnpgbe_check_mac_link_hw_ops(struct rnpgbe_hw *hw,
					rnpgbe_link_speed *speed,
					bool *link_up,
					bool *duplex,
					bool link_up_wait_to_complete)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;

	if (hw->speed == 10)
		*speed = RNP_LINK_SPEED_10_FULL;
	else if (hw->speed == 100)
		*speed = RNP_LINK_SPEED_100_FULL;
	else if (hw->speed == 1000)
		*speed = RNP_LINK_SPEED_1GB_FULL;
	else if (hw->speed == 10000)
		*speed = RNP_LINK_SPEED_10GB_FULL;
	else if (hw->speed == 25000)
		*speed = RNP_LINK_SPEED_25GB_FULL;
	else if (hw->speed == 40000)
		*speed = RNP_LINK_SPEED_40GB_FULL;
	else
		*speed = RNP_LINK_SPEED_UNKNOWN;

	*link_up = hw->link;

	if (adapter->priv_flags & RNP_PRIV_FLGA_TEST_TX_HANG)
		*link_up = 0;

	*duplex = hw->duplex;

	return 0;
}

static s32 rnpgbe_setup_mac_link_hw_ops(struct rnpgbe_hw *hw,
					u32 adv,
					u32 autoneg,
					u32 speed, u32 duplex)
{
	rnpgbe_mbx_phy_link_set(hw, adv, autoneg, speed, duplex,
				hw->tp_mdix_ctrl);

	return 0;
}

static void rnpgbe_clean_link_hw_ops(struct rnpgbe_hw *hw)
{
	hw->link = 0;
}

static void rnpgbe_set_layer2_hw_ops(struct rnpgbe_hw *hw,
				     union rnpgbe_atr_input *input,
				     u16 pri_id, u8 queue, bool prio_flag)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_layer2_remapping(eth, input, pri_id, queue, prio_flag);
}

static void rnpgbe_clr_layer2_hw_ops(struct rnpgbe_hw *hw, u16 pri_id)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.clr_layer2_remapping(eth, pri_id);
}

static void rnpgbe_clr_all_layer2_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.clr_all_layer2_remapping(eth);
}

static void rnpgbe_clr_all_tuple5_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.clr_all_tuple5_remapping(eth);
}

static void rnpgbe_set_tcp_sync_hw_ops(struct rnpgbe_hw *hw, int queue,
				       bool flag, bool prio)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_tcp_sync_remapping(eth, queue, flag, prio);
}

static void rnpgbe_set_rx_skip_hw_ops(struct rnpgbe_hw *hw, int count,
				      bool flag)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_rx_skip(eth, count, flag);
}

static void rnpgbe_set_outer_vlan_type_hw_ops(struct rnpgbe_hw *hw,
					      int type)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_outer_vlan_type(eth, type);
}

/**
 * rnpgbe_get_thermal_sensor_data_hw_ops - Gathers thermal sensor data
 * @hw: pointer to hardware structure
 * Returns the thermal sensor data structure
 **/
static s32 rnpgbe_get_thermal_sensor_data_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_thermal_sensor_data *data = &hw->thermal_sensor_data;
	int voltage = 0;

	data->sensor[0].temp = rnpgbe_mbx_get_temp(hw, &voltage);

	return 0;
}

/**
 * rnpgbe_init_thermal_sensor_thresh_hw_ops - Inits thermal sensor thresholds
 * @hw: pointer to hardware structure
 * Inits the thermal sensor thresholds according to the NVM map
 * and save off the threshold and location values into mac.thermal_sensor_data
 **/
static s32 rnpgbe_init_thermal_sensor_thresh_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_thermal_sensor_data *data = &hw->thermal_sensor_data;
	u8 i;

	for (i = 0; i < RNPGBE_MAX_SENSORS; i++) {
		data->sensor[i].location = i + 1;
		data->sensor[i].caution_thresh = 90;
		data->sensor[i].max_op_thresh = 100;
	}

	return 0;
}


static s32 rnpgbe_phy_read_reg_hw_ops(struct rnpgbe_hw *hw,
				      u32 reg_addr,
				      u32 device_type,
				      u16 *phy_data)
{
	struct rnpgbe_mac_info *mac = &hw->mac;
	s32 status = 0;
	u32 data = 0;

	status = mac->ops.mdio_read(mac, reg_addr, &data);
	*phy_data = data & 0xffff;

	return status;
}

static s32 rnpgbe_phy_write_reg_hw_ops(struct rnpgbe_hw *hw,
				       u32 reg_addr,
				       u32 device_type,
				       u16 phy_data)
{
	struct rnpgbe_mac_info *mac = &hw->mac;
	s32 status = 0;

	status = mac->ops.mdio_write(mac, reg_addr, (u32)phy_data);

	return status;
}

static void rnpgbe_setup_wol_hw_ops(struct rnpgbe_hw *hw,
				    u32 mode)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	mac->ops.pmt(mac, mode, !!hw->ncsi_en);
}

static void rnpgbe_setup_eee_hw_ops(struct rnpgbe_hw *hw,
				    int ls, int tw,
				    u32 local_eee)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	mac->ops.set_eee_timer(mac, ls, tw);
	rnpgbe_mbx_phy_eee_set(hw, tw, local_eee);
}

static void rnpgbe_set_eee_mode_hw_ops(struct rnpgbe_hw *hw,
				       bool en_tx_lpi_clockgating)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	mac->ops.set_eee_mode(mac, en_tx_lpi_clockgating);
}

static void rnpgbe_reset_eee_mode_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	mac->ops.reset_eee_mode(mac);
}

static void rnpgbe_set_eee_pls_hw_ops(struct rnpgbe_hw *hw,
				      int link)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	mac->ops.set_eee_pls(mac, link);
}

static u32 rnpgbe_get_lpi_status_hw_ops(struct rnpgbe_hw *hw)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	return mac->ops.get_lpi_status(mac);
}

static int rnpgbe_get_ncsi_mac_hw_ops(struct rnpgbe_hw *hw,
				      u8 *addr, int idx)
{
#define NCSI_MAC_H(i) (0x48 + i * 0x8)
#define NCSI_MAC_L(i) (0x4C + i * 0x8)
	struct rnpgbe_mac_info *mac = &hw->mac;
	u32 rar_h, rar_l;

	rar_h = mac_rd32(mac, NCSI_MAC_H(idx));
	rar_l = mac_rd32(mac, NCSI_MAC_L(idx));

	if (((rar_h & 0x0000ffff) != 0x0000ffff) || (rar_l != 0xffffffff)) {
		*(addr + 3) = (rar_l & 0xff000000) >> 24;
		*(addr + 2) = (rar_l & 0xff0000) >> 16;
		*(addr + 1) = (rar_l & 0xff00) >> 8;
		*(addr + 0) = (rar_l & 0xff) >> 0;
		*(addr + 5) = (rar_h & 0xff00) >> 8;
		*(addr + 4) = (rar_h & 0xff) >> 0;
		return 0;
	} else
		return -1;
}

static int rnpgbe_get_ncsi_vlan_hw_ops(struct rnpgbe_hw *hw,
				       u16 *vlan, int idx)
{
#define NCSI_VLAN(i) (0x80 + i * 0x10)
	struct rnpgbe_mac_info *mac = &hw->mac;
	u32 vid;

	vid = mac_rd32(mac, NCSI_VLAN(idx));

	if (vid & 0x80000000) {
		*vlan = (u16)(vid & 0x0000ffff);
		return 0;
	} else
		return -1;
}

static void rnpgbe_set_lldp_hw_ops(struct rnpgbe_hw *hw,
				   bool enable)
{
	rnpgbe_mbx_lldp_set(hw, enable);
}

static void rnpgbe_get_lldp_hw_ops(struct rnpgbe_hw *hw)
{
}

static void rnpgbe_dump_rings_regs(struct rnpgbe_hw *hw)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct net_device *netdev = adapter->netdev;
	struct device *dev = &hw->pdev->dev;
	struct rnpgbe_ring *ring;
	u32 head = 0;
	u32 tail = 0;
	int i;

	for (i = 0; i < RNP_NUM_TX_QUEUES; i++) {
		ring = adapter->tx_ring[i];
		head = ring_rd32(ring, RNP_DMA_REG_TX_DESC_BUF_HEAD),
		tail = ring_rd32(ring, RNP_DMA_REG_TX_DESC_BUF_TAIL),
		dev_info(dev,
			 "\tTxq-%-3u "
			 "(0x%08x)-head : (%4u),\t"
			 "(0x%08x)-tail : (%4u),\t"
			 "ntu  : (%4u),\t"
			 "ntc  : (%4u),\t"
			 "\n",
			 ring->rnpgbe_queue_idx,
			 RNPGBE_RING_BASE + RING_OFFSET(ring->rnpgbe_queue_idx) +
			 RNP_DMA_REG_TX_DESC_BUF_HEAD,
			 head,
			 RNPGBE_RING_BASE + RING_OFFSET(ring->rnpgbe_queue_idx) +
			 RNP_DMA_REG_TX_DESC_BUF_TAIL,
			 tail,
			 ring->next_to_use,
			 ring->next_to_clean);
	}

	for (i = 0; i < RNP_NUM_RX_QUEUES; i++) {
		ring = adapter->rx_ring[i];
		head = ring_rd32(ring, RNP_DMA_REG_RX_DESC_BUF_HEAD),
		tail = ring_rd32(ring, RNP_DMA_REG_RX_DESC_BUF_TAIL),
		dev_info(dev,
			 "\tTxq-%-3u "
			 "(0x%08x)-head : (%4u),\t"
			 "(0x%08x)-tail : (%4u),\t"
			 "ntu  : (%4u),\t"
			 "ntc  : (%4u)\t"
			 "\n",
			 ring->rnpgbe_queue_idx,
			 RNPGBE_RING_BASE + RING_OFFSET(ring->rnpgbe_queue_idx) +
			 RNP_DMA_REG_RX_DESC_BUF_HEAD,
			 head,
			 RNPGBE_RING_BASE + RING_OFFSET(ring->rnpgbe_queue_idx) +
			 RNP_DMA_REG_RX_DESC_BUF_TAIL,
			 tail,
			 ring->next_to_use,
			 ring->next_to_clean);
	}
}

static const struct rnpgbe_debug_reg tx_debug_reg_eth[] = {
	{"3to1(in from host)", RNPGBE_ETH_3TO1_HOST},
	{"3to1(in from sw)", RNPGBE_ETH_3TO1_SW},
	{"3to1(in from bmc)", RNPGBE_ETH_3TO1_BMC},
	{"3to1(out all)", RNPGBE_ETH_3TO1_OUT},
	{"out (multiple)", RNPGBE_ETH_OUT_MULTIPLE},
	{"out (broadcast)", RNPGBE_ETH_OUT_BROADCAST},
	{"out (ptp)", RNPGBE_ETH_OUT_PTP},
	{"out (drop)", RNPGBE_ETH_OUT_DROP},
	{"tx_trans", RNPGBE_ETH_TX_TRANS},
	{"tx_trans_status_0", RNPGBE_ETH_TX_TRANS_STATUS_0},
	{"tx_trans_status_1", RNPGBE_ETH_TX_TRANS_STATUS_1},
	{"tx_trans_sop", RNPGBE_ETH_TX_TRANS_SOP},
	{"tx_trans_eop", RNPGBE_ETH_TX_TRANS_EOP}
};

static const struct rnpgbe_debug_reg tx_debug_reg_mac[] = {
	{"mac_status", 0}
};

static void rnpgbe_dump_tx_regs(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_mac_info *mac = &hw->mac;
	struct device *dev = &hw->pdev->dev;
	u32 value;
	int i;

	/* eth */
	for (i = 0; i < sizeof(tx_debug_reg_eth) / sizeof(struct rnpgbe_debug_reg); i++) {
		value = eth_rd32(eth, tx_debug_reg_eth[i].offset);
		dev_info(dev,
			"\t%s \t:0x%08x(%4u)",
			tx_debug_reg_eth[i].name,
			value, value);
	}

	/* mac */
	for (i = 0; i < sizeof(tx_debug_reg_mac) / sizeof(struct rnpgbe_debug_reg); i++) {
		value = mac_rd32(mac, tx_debug_reg_mac[i].offset);
		dev_info(dev,
			"\t%s \t:0x%08x(%4u)",
			tx_debug_reg_mac[i].name,
			value, value);
	}
}

static const struct rnpgbe_debug_reg rx_debug_reg_trans[] = {
	{"pkts-in", RNPGBE_ETH_PKTS_IN},
	{"pkts-out", RNPGBE_ETH_PKTS_OUT},
	{"pkts-drop(fifo full or abnormal)", RNPGBE_ETH_PKTS_DRIP},
	{"pkts-in(ethII)", RNPGBE_ETH_PKTS_IN_ETH2},
	{"pkts-in(802.3)", RNPGBE_ETH_PKTS_IN_8023},
	{"pkts-in(control)", RNPGBE_ETH_PKTS_IN_CONTROL},
	{"pkts-in(udp)", RNPGBE_ETH_PKTS_IN_UDP},
	{"pkts-in(tcp)", RNPGBE_ETH_PKTS_IN_TCP},
	{"pkts-in(icmp)", RNPGBE_ETH_PKTS_IN_ICMP},
	{"pkts-in(lcs_err)", RNPGBE_ETH_PKTS_IN_LCS_ERR},
	{"pkts-in(len_err)", RNPGBE_ETH_PKTS_IN_LEN_ERR},
	{"pkts-in(dmac_failed)", RNPGBE_ETH_PKTS_IN_DMAC_F},
	{"pkts-in(smac_failed)", RNPGBE_ETH_PKTS_IN_SMAC_F},
	{"pkts-in(slen_err)", RNPGBE_ETH_PKTS_IN_SLEN_ERR},
	{"pkts-in(glen_err)", RNPGBE_ETH_PKTS_IN_GLEN_ERR},
	{"pkts-in(iph_err)", RNPGBE_ETH_PKTS_IN_IPH_ERR},
	{"pkts-in(payload_err)", RNPGBE_ETH_PKTS_IN_PAYLOAD_ERR},
	{"pkts-in(ipv4)", RNPGBE_ETH_PKTS_IN_IPV4},
	{"pkts-in(ipv6)", RNPGBE_ETH_PKTS_IN_IPV6},
	{"pkts-in(cut_err)", RNPGBE_ETH_PKTS_IN_CUT_ERR},
	{"pkts-in(except_bytes)", RNPGBE_ETH_PKTS_IN_EXCEPT_BYTES},
	{"pkts-in(fcs_err)", RNPGBE_ETH_PKTS_IN_FCS_ERR},
	{"pkts-in(mac_len_err)", RNPGBE_ETH_PKTS_IN_MAC_LEN_ERR}
};

static const struct rnpgbe_debug_reg rx_debug_reg_gather[] = {
	{"pkts-in", RNPGBE_GATHER_PKTS_IN},
	{"pkts-out", RNPGBE_GATHER_PKTS_OUT},
	{"pkts-out(mutiplecast)", RNPGBE_GATHER_PKTS_OUT_MUL},
	{"pkts-out(broadcast)", RNPGBE_GATHER_PKTS_OUT_BRO},
	{"pkts(drop)", RNPGBE_GATHER_PKTS_IN_DROP},
	{"pkts-in(mac_cut)", RNPGBE_GATHER_PKTS_IN_MAC_CUT},
	{"pkts-in(mac_lcs_err)", RNPGBE_GATHER_PKTS_IN_MAC_LCS_ERR},
	{"pkts-in(mac_len_err)", RNPGBE_GATHER_PKTS_IN_MAC_LEN_ERR},
	{"pkts-in(mac_slen_err)", RNPGBE_GATHER_PKTS_IN_MAC_SLEN_ERR},
	{"pkts-in(mac_glen_err)", RNPGBE_GATHER_PKTS_IN_MAC_GLEN_ERR},
	{"pkts-in(mac_fcs_err)", RNPGBE_GATHER_PKTS_IN_MAC_FCS_ERR},
	{"pkts-in(mac<64byts fcs_err)", RNPGBE_GATHER_PKTS_IN_SMALL_64},
	{"pkts-in(mac>=64byts fcs_err)", RNPGBE_GATHER_PKTS_IN_LARGE_64}
};

static const struct rnpgbe_debug_reg rx_debug_reg_pip_parse[] = {
	{"pkts-in", RNPGBE_PARSE_PKTS_IN},
	{"pkts-out", RNPGBE_PARSE_PKTS_OUT},
	{"pkts(arp request)", RNPGBE_PARSE_PKTS_ARP_REQUEST},
	{"pkts(arp response)", RNPGBE_PARSE_PKTS_ARP_RESPONS},
	{"pkts(icmp)", RNPGBE_PARSE_PKTS_ICMP},
	{"pkts(udp)", RNPGBE_PARSE_PKTS_UDP},
	{"pkts(tcp)", RNPGBE_PARSE_PKTS_TCP},
	{"pkts(arp cut)", RNPGBE_PARSE_PKTS_ARP_CUT},
	{"pkts(ND cut)", RNPGBE_PARSE_PKTS_ND_CUT},
	{"pkts(sctp)", RNPGBE_PARSE_PKTS_SCTP},
	{"pkts(tcp syn)", RNPGBE_PARSE_PKTS_TCP_SYN},
	{"pkts(fragment)", RNPGBE_PARSE_PKTS_FRAGMENT},
	{"pkts(1 vlan)", RNPGBE_PARSE_PKTS_1_VLAN},
	{"pkts(2 vlans)", RNPGBE_PARSE_PKTS_2_VLANS},
	{"pkts(ipv4)", RNPGBE_PARSE_PKTS_IPV4},
	{"pkts(ipv6)", RNPGBE_PARSE_PKTS_IPV6},
	{"pkts(ip hdr err)", RNPGBE_PARSE_PKTS_IP_HDR_ERR},
	{"pkts(ip pkt err)", RNPGBE_PARSE_PKTS_IP_PKT_ERR},
	{"pkts(l3 hdr chk err)", RNPGBE_PARSE_PKTS_L3_HDR_CHK_ERR},
	{"pkts(l4 hdr chk err)", RNPGBE_PARSE_PKTS_L4_HDR_CHK_ERR},
	{"pkts(sctp hdr chk err)", RNPGBE_PARSE_PKTS_SCTP_HDR_CHK_ERR},
	{"pkts(vlan err)", RNPGBE_PARSE_PKTS_VLAN_ERR},
	{"pkts(rdma)", RNPGBE_PARSE_PKTS_RDMA},
	{"pkts(arp auto response)", RNPGBE_PARSE_PKTS_ARP_AUTO_RESP},
	{"pkts(icmpv6)", RNPGBE_PARSE_PKTS_ICMPV6},
	{"pkts(ipv6 extend)", RNPGBE_PARSE_PKTS_IPV6_EXTEND},
	{"pkts(802.3)", RNPGBE_PARSE_PKTS_8023},
	{"pkts(except short)", RNPGBE_PARSE_PKTS_EXCEPT_SHORt},
	{"pkts(ptp)", RNPGBE_PARSE_PKTS_PTP},
	{"pkts(NS req)", RNPGBE_PARSE_PKTS_NS_REQ},
	{"pkts(NS_NA auto res)", RNPGBE_PARSE_PKTS_NS_NA_AUTO_RES}
};

static const struct rnpgbe_debug_reg rx_debug_reg_pip_decap[] = {
	{"pkts-in(all)", 0x82d0},
	{"pkts-out(all)", 0x82d4},
	{"pkts-out(host)", 0x82d8},
	{"pkts-out(bmc)", 0x82dc},
	{"pkts-out(switch)", 0x82e0},
	{"pkts-out(bmc+host)", 0x82e4},
	{"pkts(drop invalid)", 0x82e8},
	{"pkts(drop filter)", 0x82ec},
	{"pkts(drop host Insufficient perf)", 0x82f0},
	{"pkts(drop bmc Insufficient perf)", 0x82f4},
	{"pkts(drop switch Insufficient perf)", 0x82f8},
	{"pkts(rm vlan)", 0x82fc}
};

static const struct rnpgbe_debug_reg_bits regs_debug[] = {
	{0x3fff, 0x8448,
		{"gat_fifo_progfull", "gat_info_fifo_progfull",
		 "parse_fifo_progfull", "parse_info_fifo_progfull",
		 "res", "cov_fifo_progfull", "host_fifo_progfull",
		 "host_info_fifo_progfull", "sw_fifo_progfull",
		 "sw_info_fifo_progfull", "bmc_fifo_progfull",
		 "bmc_info_fifo_progfull", "mac_rxdatafifo_progfull",
		 "mac_rxinfo_fifo_progfull", " ", " ", " ", " ",
		 " ", " ", " ", " ", " ", " ", " ", " "
		 " ", " ", " ", " ", " ", " "
		},
	},

	{0x3c2f, 0x844c,
		{"gat_fifo_full", "gat_info_fifo_full",
		 "parse_fifo_full", "parse_info_fifo_full",
		 " ", "cov_fifo_progfull", " ",
		 " ", " ",
		 " ", "bmc_fifo_full",
		 "bmc_info_fifo_full", "mac_rxdatafifo_full",
		 "mac_rxinfo_fifo_full", " ", " ", " ", " ",
		 " ", " ", " ", " ", " ", " ", " ", " "
		 " ", " ", " ", " ", " ", " "
		},
	}
};

static void rnpgbe_dump_rx_regs(struct rnpgbe_hw *hw)
{
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct device *dev = &hw->pdev->dev;
	u32 value;
	int i;

	/* rx trans */
	dev_info(dev, "\trx trans module:\n");
	for (i = 0; i < sizeof(rx_debug_reg_trans) / sizeof(struct rnpgbe_debug_reg); i++) {
		value = eth_rd32(eth, rx_debug_reg_trans[i].offset);
		dev_info(dev,
			"\t%s \t:0x%08x(%4u)",
			rx_debug_reg_trans[i].name,
			value, value);
	}

	/* emac gater */
	dev_info(dev, "\temac gather module:\n");
	for (i = 0; i < sizeof(rx_debug_reg_gather) / sizeof(struct rnpgbe_debug_reg); i++) {
		value = eth_rd32(eth, rx_debug_reg_gather[i].offset);
		dev_info(dev,
			"\t%s \t:0x%08x(%4u)",
			rx_debug_reg_gather[i].name,
			value, value);
	}

	/* pip parse */
	dev_info(dev, "\tpip parse module:\n");
	for (i = 0; i < sizeof(rx_debug_reg_pip_parse) / sizeof(struct rnpgbe_debug_reg); i++) {
		value = eth_rd32(eth, rx_debug_reg_pip_parse[i].offset);
		dev_info(dev,
			"\t%s \t:0x%08x(%4u)",
			rx_debug_reg_pip_parse[i].name,
			value, value);
	}
	/* pip decap */
	dev_info(dev, "\tpip decap module:\n");
	for (i = 0; i < sizeof(rx_debug_reg_pip_decap) / sizeof(struct rnpgbe_debug_reg); i++) {
		value = eth_rd32(eth, rx_debug_reg_pip_decap[i].offset);
		dev_info(dev,
			"\t%s \t:0x%08x(%4u)",
			rx_debug_reg_pip_decap[i].name,
			value, value);
	}

	dev_info(dev, "\tdebug reg:\n");
	for (i = 0; i < sizeof(regs_debug) / sizeof(struct rnpgbe_debug_reg_bits); i++) {
		int j;

		value = eth_rd32(eth, regs_debug[i].offset);
		/* for each bits */
		for (j = 0; j < 32; j++) {
			/* only check flags set value */
			if ((value & regs_debug[i].flags) & (1 << j)) {
				dev_info(dev,
					"\t%s detected\n", regs_debug[i].name[j]);
			}
		}
	}
	dev_info(dev, "\tend\n");

}

static void rnpgbe_check_status(struct rnpgbe_hw *hw)
{
	// todo
}

static void rnpgbe_dump_gephy(struct rnpgbe_hw *hw)
{
	struct device *dev = &hw->pdev->dev;
	u16 page = 0;
	u32 reg = 0;
	u16 value;

	for (page = 0; page < 0x12; page++) {
		/* write page */
		hw->ops.phy_write_reg(hw, 0x1f, 0, page);

		for (reg = 0; reg < 31; reg++) {
			hw->ops.phy_read_reg(hw, reg, 0, &value);
			dev_info(dev, "page %02d reg 0x%02x:0x%04x\n", page, reg, value);
		}
	}
	hw->ops.phy_write_reg(hw, 0x1f, 0, 0);

}

static int rnpgbe_dump_debug_regs_hw_ops(struct rnpgbe_hw *hw,
		char *cmd)
{
	int ret = -1;

	if (!strncmp(cmd, "ring", 4)) {
		rnpgbe_dump_rings_regs(hw);
		ret = 0;
	} else if (!strncmp(cmd, "tx", 2)) {
		rnpgbe_dump_tx_regs(hw);
		ret = 0;
	} else if (!strncmp(cmd, "rx", 2)) {
		rnpgbe_dump_rx_regs(hw);
		ret = 0;
	} else if (!strncmp(cmd, "check", 5)) {
		rnpgbe_check_status(hw);
	} else if (!strncmp(cmd, "gephy", 5)) {
		rnpgbe_dump_gephy(hw);
		ret = 0;
	}
	return ret;
}

static void rnpgbe_set_eee_timer_hw_ops(struct rnpgbe_hw *hw,
					int ls, int tw)
{
	struct rnpgbe_mac_info *mac = &hw->mac;

	mac->ops.set_eee_timer(mac, ls, tw);
}

static void rnpgbe_set_vf_vlan_mode_hw_ops(struct rnpgbe_hw *hw,
					   u16 vlan, int vf,
					   bool enable)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_eth_info *eth = &hw->eth;

	if (adapter->priv_flags & RNP_PRIV_FLAG_SRIOV_VLAN_MODE)
		eth->ops.set_vf_vlan_mode(eth, vlan, vf, enable);
}

static void rnpgbe_driver_status_hw_ops(struct rnpgbe_hw *hw,
					bool enable,
					int mode)
{
	switch (mode) {
	case rnpgbe_driver_insmod:
		rnpgbe_mbx_ifinsmod(hw, enable);
		break;
	case rnpgbe_driver_suspuse:
		rnpgbe_mbx_ifsuspuse(hw, enable);
		break;
	case rnpgbe_driver_force_control_phy:
		rnpgbe_mbx_ifforce_control_mac(hw, enable);

		break;
	}
}

static void rnpgbe_set_tuple5_hw_ops(struct rnpgbe_hw *hw,
				     union rnpgbe_atr_input *input,
				     u16 pri_id, u8 queue, bool prio_flag)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.set_tuple5_remapping(eth, input, pri_id, queue, prio_flag);
}

static void rnpgbe_clr_tuple5_hw_ops(struct rnpgbe_hw *hw, u16 pri_id)
{
	struct rnpgbe_eth_info *eth = &hw->eth;

	eth->ops.clr_tuple5_remapping(eth, pri_id);
}

static void rnpgbe_update_hw_status_hw_ops(struct rnpgbe_hw *hw,
					   struct rnpgbe_hw_stats *hw_stats,
					   struct net_device_stats *net_stats)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_mac_info *mac = &hw->mac;
	u64 mac_rx_broadcast = 0;
	u64 mac_rx_multicast = 0;
	u64 rx_over_errors = 0;
	int i;

	net_stats->rx_length_errors = eth_rd32(eth, RNPGBE_RXTRANS_LEN_ERR_NUM) +
				eth_rd32(eth, RNPGBE_RXTRANS_SLEN_ERR_NUM) +
				eth_rd32(eth, RNPGBE_RXTRANS_GLEN_ERR_NUM);
	net_stats->rx_crc_errors = eth_rd32(eth, RNPGBE_RXTRANS_FCS_ERR_NUM);
	net_stats->rx_frame_errors = eth_rd32(eth, RNPGBE_RXTRANS_LCS_ERR_NUM);
	net_stats->rx_fifo_errors = eth_rd32(eth, RNPGBE_RXTRANS_DROP);
	net_stats->rx_missed_errors = net_stats->rx_fifo_errors +
				eth_rd32(eth, RNPGBE_RXTRANS_CUT_ERR_PKTS) +
				eth_rd32(eth, RNPGBE_RXTRANS_EXCEPT_NUM);
	net_stats->collisions = eth_rd32(eth, RNPGBE_RX_MAC_LCS_ERR_NUM);
	hw_stats->invalid_droped_packets =
		eth_rd32(eth, RNPGBE_RX_DROP_PKT_NUM);
	hw_stats->rx_capabity_lost = eth_rd32(eth, RNPGBE_RXTRANS_DROP) +
				     eth_rd32(eth, RNPGBE_RXTRANS_CUT_ERR_PKTS);
	hw_stats->filter_dropped_packets =
		eth_rd32(eth, RNPGBE_DECAP_PKT_DROP1_NUM);
	hw_stats->host_l2_match_drop =
		eth_rd32(eth, RNPGBE_ETH_HOST_L2_DROP_PKTS);
	hw_stats->redir_input_match_drop =
		eth_rd32(eth, RNPGBE_ETH_REDIR_INPUT_MATCH_DROP_PKTS);
	hw_stats->redir_etype_match_drop =
		eth_rd32(eth, RNPGBE_ETH_ETYPE_DROP_PKTS);
	hw_stats->redir_tcp_syn_match_drop =
		eth_rd32(eth, RNPGBE_ETH_TCP_SYN_DROP_PKTS);
	hw_stats->redir_tuple5_match_drop =
		eth_rd32(eth, RNPGBE_ETH_REDIR_TUPLE5_DROP_PKTS);
	hw_stats->tx_multicast = eth_rd32(eth, RNPGBE_TX_MULTI_NUM);
	hw_stats->tx_broadcast = eth_rd32(eth, RNPGBE_TX_BROADCAST_NUM);

	for (i = 0; i < adapter->num_tx_queues; i++) {
		struct rnpgbe_ring *tx_ring = adapter->tx_ring[i];
		int idx = tx_ring->rnpgbe_queue_idx;

		rx_over_errors += dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(idx));
		mac_rx_multicast +=
			dma_rd32(dma, RNPGBE_VEB_VFMPRC(idx));
		mac_rx_broadcast +=
			dma_rd32(dma, RNPGBE_VEB_VFBPRC(idx));
	}

	hw_stats->mac_rx_broadcast = mac_rx_broadcast;
	hw_stats->mac_rx_multicast = mac_rx_multicast;

	net_stats->rx_over_errors = rx_over_errors;
	net_stats->rx_errors = net_stats->rx_length_errors +
			       net_stats->rx_over_errors +
			       net_stats->rx_crc_errors +
			       net_stats->rx_frame_errors +
			       net_stats->rx_fifo_errors +
			       net_stats->rx_missed_errors;

	hw_stats->dma_rx_drop_cnt_0 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(0));
	hw_stats->dma_rx_drop_cnt_1 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(1));
	hw_stats->dma_rx_drop_cnt_2 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(2));
	hw_stats->dma_rx_drop_cnt_3 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(3));
	hw_stats->dma_rx_drop_cnt_4 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(4));
	hw_stats->dma_rx_drop_cnt_5 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(5));
	hw_stats->dma_rx_drop_cnt_6 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(6));
	hw_stats->dma_rx_drop_cnt_7 = dma_rd32(dma, RNPGBE_RX_TIMEOUT_DROP(7));

	net_stats->multicast = hw_stats->mac_rx_multicast;

	hw_stats->ultra_short_cnt +=
		mac_rd32(mac, GMAC_MANAGEMENT_RX_UNDERSIZE);
	hw_stats->jumbo_cnt += mac_rd32(mac, RNPGBE_MAC_GLEN_ERR_NUM);
	hw_stats->tx_pause += mac_rd32(mac, GMAC_MANAGEMENT_TX_PAUSE);
	hw_stats->rx_pause += mac_rd32(mac, GMAC_MANAGEMENT_RX_PAUSE);
}

static const struct rnpgbe_stats rnpgbe_gstrings_net_stats[] = {
	RNP_NETDEV_STAT(rx_packets),
	RNP_NETDEV_STAT(tx_packets),
	RNP_NETDEV_STAT(rx_bytes),
	RNP_NETDEV_STAT(tx_bytes),
	RNP_NETDEV_STAT(rx_errors),
	RNP_NETDEV_STAT(tx_errors),
	RNP_NETDEV_STAT(rx_dropped),
	RNP_NETDEV_STAT(tx_dropped),
	RNP_NETDEV_STAT(multicast),
	RNP_NETDEV_STAT(collisions),
	RNP_NETDEV_STAT(rx_over_errors),
	RNP_NETDEV_STAT(rx_crc_errors),
	RNP_NETDEV_STAT(rx_frame_errors),
	RNP_NETDEV_STAT(rx_fifo_errors),
	RNP_NETDEV_STAT(rx_missed_errors),
	RNP_NETDEV_STAT(tx_aborted_errors),
	RNP_NETDEV_STAT(tx_carrier_errors),
	RNP_NETDEV_STAT(tx_fifo_errors),
	RNP_NETDEV_STAT(tx_heartbeat_errors),
};

#define RNPGBE_GLOBAL_STATS_LEN ARRAY_SIZE(rnpgbe_gstrings_net_stats)

static struct rnpgbe_stats rnpgbe_hwstrings_stats[] = {
	RNP_HW_STAT("vlan_add_cnt", hw_stats.vlan_add_cnt),
	RNP_HW_STAT("vlan_strip_cnt", hw_stats.vlan_strip_cnt),
	RNP_HW_STAT("invalid_droped_packets", hw_stats.invalid_droped_packets),
	RNP_HW_STAT("rx_capabity_drop", hw_stats.rx_capabity_lost),
	RNP_HW_STAT("filter_dropped_packets", hw_stats.filter_dropped_packets),
	RNP_HW_STAT("host_l2_match_drop", hw_stats.host_l2_match_drop),
	RNP_HW_STAT("redir_input_match_drop", hw_stats.redir_input_match_drop),
	RNP_HW_STAT("redir_etype_match_drop", hw_stats.redir_etype_match_drop),
	RNP_HW_STAT("redir_tcp_syn_match_drop",
		    hw_stats.redir_tcp_syn_match_drop),
	RNP_HW_STAT("redir_tuple5_match_drop",
		    hw_stats.redir_tuple5_match_drop),
	RNP_HW_STAT("tx_multicast", hw_stats.tx_multicast),
	RNP_HW_STAT("tx_broadcast", hw_stats.tx_broadcast),
	RNP_HW_STAT("rx_csum_offload_errors", hw_csum_rx_error),
	RNP_HW_STAT("rx_csum_offload_good", hw_csum_rx_good),
	RNP_HW_STAT("rx_broadcast_count", hw_stats.mac_rx_broadcast),
	RNP_HW_STAT("rx_multicast_count", hw_stats.mac_rx_multicast),
	RNP_HW_STAT("ultra_short_packets", hw_stats.ultra_short_cnt),
	RNP_HW_STAT("jumbo_packets", hw_stats.jumbo_cnt),
	RNP_HW_STAT("mac_rx_pause_count", hw_stats.rx_pause),
	RNP_HW_STAT("mac_tx_pause_count", hw_stats.tx_pause),
};

#define RNPGBE_HWSTRINGS_STATS_LEN ARRAY_SIZE(rnpgbe_hwstrings_stats)

#define RNPGBE_STATS_LEN                                                       \
	(RNPGBE_GLOBAL_STATS_LEN + RNPGBE_HWSTRINGS_STATS_LEN +                \
	 RNP_QUEUE_STATS_LEN)

#ifndef CLOST_SELF_TEST
#ifdef ETHTOOL_TEST
static const char rnpgbe_gstrings_test[][ETH_GSTRING_LEN] = {
	"Register test  (offline)", "Eeprom test    (offline)",
	"Interrupt test (offline)", "Loopback test  (offline)",
	"Link test   (on/offline)"
};

#define RNPGBE_TEST_LEN (sizeof(rnpgbe_gstrings_test) / ETH_GSTRING_LEN)
#else
#define RNPGBE_TEST_LEN 0
#endif
#else
#define RNPGBE_TEST_LEN 0
#endif

#if defined(ETHTOOL_GLINKSETTINGS) && !defined(KYLIN_V4_ETHTOOL_FIX_BOND)
static int rnpgbe_get_link_ksettings(struct net_device *netdev,
				     struct ethtool_link_ksettings *cmd)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;
	rnpgbe_link_speed advertised_link;
	rnpgbe_link_speed supported_link;
	bool autoneg = hw->autoneg;

	ethtool_link_ksettings_zero_link_mode(cmd, supported);
	ethtool_link_ksettings_zero_link_mode(cmd, advertising);

	supported_link = hw->supported_link;
	advertised_link = hw->advertised_link;

	if (hw->is_sgmii) {
		if (supported_link & RNP_LINK_SPEED_1GB_FULL)
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     1000baseT_Full);
		if (supported_link & RNP_LINK_SPEED_100_FULL)
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     100baseT_Full);
		if (supported_link & RNP_LINK_SPEED_10_FULL)
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     10baseT_Full);
		if (supported_link & RNP_LINK_SPEED_1GB_HALF)
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     1000baseT_Half);
		if (supported_link & RNP_LINK_SPEED_100_HALF)
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     100baseT_Half);
		if (supported_link & RNP_LINK_SPEED_10_HALF)
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     10baseT_Half);

		if ((autoneg) && (!hw->fake_autoneg)) {
			if (advertised_link & RNP_LINK_SPEED_1GB_FULL)
				ethtool_link_ksettings_add_link_mode(
					cmd, advertising, 1000baseT_Full);
			if (advertised_link & RNP_LINK_SPEED_100_FULL)
				ethtool_link_ksettings_add_link_mode(
					cmd, advertising, 100baseT_Full);
			if (advertised_link & RNP_LINK_SPEED_10_FULL)
				ethtool_link_ksettings_add_link_mode(
					cmd, advertising, 10baseT_Full);
			if (advertised_link & RNP_LINK_SPEED_1GB_HALF)
				ethtool_link_ksettings_add_link_mode(
					cmd, advertising, 1000baseT_Half);
			if (advertised_link & RNP_LINK_SPEED_100_HALF)
				ethtool_link_ksettings_add_link_mode(
					cmd, advertising, 100baseT_Half);
			if (advertised_link & RNP_LINK_SPEED_10_HALF)
				ethtool_link_ksettings_add_link_mode(
					cmd, advertising, 10baseT_Half);
		}

		ethtool_link_ksettings_add_link_mode(cmd, supported, TP);

		if (!hw->fake_autoneg)
			ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);

		cmd->base.port = PORT_TP;
		cmd->base.phy_address = adapter->phy_addr;
		cmd->base.duplex = adapter->duplex;
		if (adapter->hw.link)
			cmd->base.eth_tp_mdix = hw->tp_mdx;
		else
			cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;

		//if (hw->fake_autoneg)
		//	cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
#ifdef ETH_TP_MDI_AUTO
		cmd->base.eth_tp_mdix_ctrl = hw->tp_mdix_ctrl;
		//if (hw->fake_autoneg)
		//	cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
#endif /* ETH_TP_MDI_AUTO */
	} else {
		if (supported_link & RNP_LINK_SPEED_1GB_FULL) {
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     1000baseKX_Full);
		}

		if (advertised_link & RNP_LINK_SPEED_1GB_FULL)
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
							     1000baseKX_Full);
		ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
		ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);

		cmd->base.port = PORT_FIBRE;
	}

	ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);

	if ((autoneg) && (!hw->fake_autoneg)) {
		ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
		cmd->base.autoneg = AUTONEG_ENABLE;
	} else
		cmd->base.autoneg = AUTONEG_DISABLE;

	if (hw->fake_autoneg)
		cmd->base.autoneg = AUTONEG_DISABLE;

	/* set pause support */
	ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
	ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause);

	if (hw->fc.requested_mode & PAUSE_AUTO)
		ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
	else {
		if ((hw->fc.requested_mode & PAUSE_TX) &&
		    (hw->fc.requested_mode & PAUSE_RX))
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
							     Pause);
		else if (hw->fc.requested_mode & PAUSE_TX)
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
							     Asym_Pause);
		else if (hw->fc.requested_mode & PAUSE_RX)
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
							     Asym_Pause);
		else
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
							     Asym_Pause);
	}

	if (adapter->hw.link) {
		cmd->base.speed = hw->speed;
		if (adapter->hw.duplex)
			cmd->base.duplex = DUPLEX_FULL;
		else
			cmd->base.duplex = DUPLEX_HALF;
	} else {
		cmd->base.speed = SPEED_UNKNOWN;
		cmd->base.duplex = DUPLEX_UNKNOWN;
	}

	return 0;
}

static int rnpgbe_set_link_ksettings(struct net_device *netdev,
				     const struct ethtool_link_ksettings *cmd)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	u32 speed = 0, autoneg = 0, duplex = 0;
	struct rnpgbe_hw *hw = &adapter->hw;
	struct device *dev = &hw->pdev->dev;
	u32 advertised, old;
	s32 err = 0;

	if ((hw->phy_type == rnpgbe_media_type_copper) ||
	    (hw->phy.multispeed_fiber)) {
		/*
		 * this function does not support duplex forcing, but can
		 * limit the advertising of the adapter to the specified speed
		 */
		/* only allow one speed at a time if no  */
		if (!cmd->base.autoneg) {
			if ((cmd->base.speed != SPEED_100) &&
			    (cmd->base.speed != SPEED_10) &&
			    (cmd->base.speed != SPEED_1000))
				return -EINVAL;
			autoneg = 0;
			speed = cmd->base.speed;
			duplex = cmd->base.duplex;
			/* if set force 1000, we should open autoneg */
			if (cmd->base.speed == SPEED_1000) {
				autoneg = 1;
				hw->fake_autoneg = 1;
			}
		} else {
			hw->fake_autoneg = 0;
			autoneg = 1;
		}

		if (cmd->base.eth_tp_mdix_ctrl) {
			if ((cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) &&
			    (cmd->base.autoneg != AUTONEG_ENABLE)) {
				dev_err(&adapter->pdev->dev,
					"forcing MDI/MDI-X state is not"
					"supported when link speed and/or duplex are forced\n");
				return -EINVAL;
			}
		}

		hw->autoneg = autoneg;
		hw->tp_mdix_ctrl = cmd->base.eth_tp_mdix_ctrl;

		old = hw->advertised_link;
		advertised = 0;

		if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
							  1000baseT_Full))
			advertised |= RNP_LINK_SPEED_1GB_FULL;

		if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
							  100baseT_Full))
			advertised |= RNP_LINK_SPEED_100_FULL;

		if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
							  10baseT_Full))
			advertised |= RNP_LINK_SPEED_10_FULL;

		if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
							  100baseT_Half))
			advertised |= RNP_LINK_SPEED_100_HALF;

		if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
							  10baseT_Half))
			advertised |= RNP_LINK_SPEED_10_HALF;

		/* if fake autoneg, adv 1G */
		if (hw->fake_autoneg)
			advertised |= RNP_LINK_SPEED_1GB_FULL;

		/* if autoneg on, adv can not set 0 */
		if ((!advertised) && (autoneg))
			return -EINVAL;

		/* this sets the link speed and restarts auto-neg */
		while (test_and_set_bit(__RNP_IN_SFP_INIT, &adapter->state))
			usleep_range(1000, 2000);

		hw->mac.autotry_restart = true;
		err = hw->ops.setup_link(hw, advertised, autoneg, speed,
					 duplex);
		if (err) {
			dev_info(dev, "setup link failed with code %d\n",
				 err);
			hw->ops.setup_link(hw, old, autoneg, speed, duplex);
		} else {
			hw->advertised_link = advertised;
		}
		clear_bit(__RNP_IN_SFP_INIT, &adapter->state);
	} else {
		/* if not sgmii, we not support close autoneg */
		if ((!cmd->base.autoneg) && (!hw->is_sgmii))
			return -EINVAL;
		if (cmd->base.duplex == DUPLEX_HALF)
			return -EINVAL;
		if (cmd->base.speed != SPEED_1000)
			return -EINVAL;
	}

	return err;
}

#else /* !ETHTOOL_GLINKSETTINGS */
static int rnpgbe_get_settings(struct net_device *netdev,
			       struct ethtool_cmd *ecmd)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;
	rnpgbe_link_speed supported_link;
	rnpgbe_link_speed advertised_link;
	bool autoneg = true;

	supported_link = hw->supported_link;
	advertised_link = hw->advertised_link;

	if (hw->is_sgmii) {
		if (supported_link & RNP_LINK_SPEED_10GB_FULL)
			ecmd->supported |= ADVERTISED_10000baseT_Full;
		if (supported_link & RNP_LINK_SPEED_1GB_FULL)
			ecmd->supported |= ADVERTISED_1000baseT_Full;
		if (supported_link & RNP_LINK_SPEED_100_FULL)
			ecmd->supported |= ADVERTISED_100baseT_Full;
		if (supported_link & RNP_LINK_SPEED_10_FULL)
			ecmd->supported |= ADVERTISED_10baseT_Full;
		if (supported_link & RNP_LINK_SPEED_1GB_HALF)
			ecmd->supported |= ADVERTISED_1000baseT_Half;
		if (supported_link & RNP_LINK_SPEED_100_HALF)
			ecmd->supported |= ADVERTISED_100baseT_Half;
		if (supported_link & RNP_LINK_SPEED_10_HALF)
			ecmd->supported |= ADVERTISED_10baseT_Half;

		if (hw->autoneg) {
			autoneg = true;
			if (!hw->fake_autoneg) {
				if (advertised_link & RNP_LINK_SPEED_10GB_FULL)
					ecmd->advertising |= ADVERTISED_10000baseT_Full;
				if (advertised_link & RNP_LINK_SPEED_1GB_FULL)
					ecmd->advertising |= ADVERTISED_1000baseT_Full;
				if (advertised_link & RNP_LINK_SPEED_100_FULL)
					ecmd->advertising |= ADVERTISED_100baseT_Full;
				if (advertised_link & RNP_LINK_SPEED_10_FULL)
					ecmd->advertising |= ADVERTISED_10baseT_Full;
				if (advertised_link & RNP_LINK_SPEED_1GB_HALF)
					ecmd->advertising |= ADVERTISED_1000baseT_Half;
				if (advertised_link & RNP_LINK_SPEED_100_HALF)
					ecmd->advertising |= ADVERTISED_100baseT_Half;
				if (advertised_link & RNP_LINK_SPEED_10_HALF)
					ecmd->advertising |= ADVERTISED_10baseT_Half;
			}
		} else {
			autoneg = false;
		}

		ecmd->supported |= SUPPORTED_TP;
		if (!hw->fake_autoneg)
			ecmd->advertising |= ADVERTISED_TP;
		ecmd->port = PORT_TP;
		ecmd->transceiver = XCVR_EXTERNAL;
		ecmd->phy_address = hw->mac.phy_addr;
		ecmd->duplex = adapter->duplex;
		//if (!hw->fake_autoneg)
		ecmd->eth_tp_mdix = hw->tp_mdx;
		//else
		//	ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
#ifdef ETH_TP_MDI_AUTO
		ecmd->eth_tp_mdix_ctrl = hw->tp_mdix_ctrl;
		//if (hw->fake_autoneg)
		ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
#endif /* ETH_TP_MDI_AUTO */
	} else {
		if (supported_link & RNP_LINK_SPEED_10GB_FULL)
			ecmd->supported |= SUPPORTED_10000baseT_Full;
		if (supported_link & RNP_LINK_SPEED_1GB_FULL)
			ecmd->supported |= SUPPORTED_1000baseKX_Full;
		if (supported_link & RNP_LINK_SPEED_25GB_FULL)
			ecmd->supported |= SUPPORTED_40000baseKR4_Full;
		if (supported_link & RNP_LINK_SPEED_40GB_FULL)
			ecmd->supported |= SUPPORTED_40000baseCR4_Full |
					   SUPPORTED_40000baseSR4_Full |
					   SUPPORTED_40000baseLR4_Full;

		if (advertised_link & RNP_LINK_SPEED_10GB_FULL)
			ecmd->advertising |= SUPPORTED_10000baseT_Full;
		if (advertised_link & RNP_LINK_SPEED_1GB_FULL)
			ecmd->advertising |= SUPPORTED_1000baseKX_Full;
		if (advertised_link & RNP_LINK_SPEED_25GB_FULL)
			ecmd->advertising |= SUPPORTED_40000baseKR4_Full;
		if (advertised_link & RNP_LINK_SPEED_40GB_FULL)
			ecmd->advertising |= SUPPORTED_40000baseCR4_Full |
					     SUPPORTED_40000baseSR4_Full |
					     SUPPORTED_40000baseLR4_Full;

		ecmd->supported |= SUPPORTED_FIBRE;
		ecmd->advertising |= ADVERTISED_FIBRE;
		ecmd->port = PORT_FIBRE;
		ecmd->transceiver = XCVR_INTERNAL;
	}

	ecmd->supported |= SUPPORTED_Autoneg;
	if ((autoneg) && (!hw->fake_autoneg)) {
		ecmd->advertising |= ADVERTISED_Autoneg;
		ecmd->autoneg = AUTONEG_ENABLE;
	} else
		ecmd->autoneg = AUTONEG_DISABLE;

	if (hw->fake_autoneg)
		ecmd->autoneg = AUTONEG_DISABLE;
	/* Indicate pause support */
	ecmd->supported |= SUPPORTED_Pause;

	if (!hw->fake_autoneg) {
		if (hw->fc.requested_mode & PAUSE_AUTO)
			ecmd->advertising |= ADVERTISED_Pause;
		else {
			if ((hw->fc.requested_mode & PAUSE_TX) &&
					(hw->fc.requested_mode & PAUSE_RX))
				ecmd->advertising |= ADVERTISED_Pause;
			else if (hw->fc.requested_mode & PAUSE_TX)
				ecmd->advertising |= ADVERTISED_Asym_Pause;
			else if (hw->fc.requested_mode & PAUSE_RX)
				ecmd->advertising |=
					ADVERTISED_Pause | ADVERTISED_Asym_Pause;
			else
				ecmd->advertising &=
					~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
		}
	}
	if (adapter->hw.duplex)
		ecmd->duplex = DUPLEX_FULL;
	else
		ecmd->duplex = DUPLEX_HALF;

	if (adapter->hw.link) {
		ethtool_cmd_speed_set(ecmd, hw->speed);
	} else {
		ethtool_cmd_speed_set(ecmd, -1);
		ecmd->duplex = DUPLEX_UNKNOWN;
	}

	return 0;
}

static int rnpgbe_set_settings(struct net_device *netdev,
			       struct ethtool_cmd *ecmd)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	u32 autoneg = 0, speed = 0, duplex = 0;
	struct rnpgbe_hw *hw = &adapter->hw;
	u32 advertised, old;
	s32 err = 0;

	if ((hw->phy_type == rnpgbe_media_type_copper) ||
	    (hw->phy.multispeed_fiber)) {
		/*
		 * this function does not support duplex forcing, but can
		 * limit the advertising of the adapter to the specified speed
		 */
		if (ecmd->advertising & ~ecmd->supported)
			return -EINVAL;

		if (ecmd->autoneg == AUTONEG_DISABLE) {
			autoneg = 0;
			if ((ecmd->speed != SPEED_100) &&
			    (ecmd->speed != SPEED_10) &&
			    (ecmd->speed != SPEED_1000))
				return -EINVAL;
			speed = ecmd->speed;
			duplex = ecmd->duplex;
			/* if set 1000 force, we open auto */
			if (ecmd->speed == SPEED_1000) {
				autoneg = 1;
				hw->fake_autoneg = 1;
			}

		} else {
			autoneg = 1;
		}
		hw->autoneg = autoneg;

		old = hw->phy.autoneg_advertised;
		advertised = 0;
		if (ecmd->advertising & ADVERTISED_10000baseT_Full)
			advertised |= RNP_LINK_SPEED_10GB_FULL;

		if (ecmd->advertising & ADVERTISED_1000baseT_Full)
			advertised |= RNP_LINK_SPEED_1GB_FULL;

		if (ecmd->advertising & ADVERTISED_100baseT_Full)
			advertised |= RNP_LINK_SPEED_100_FULL;

		if (ecmd->advertising & ADVERTISED_10baseT_Full)
			advertised |= RNP_LINK_SPEED_10_FULL;

		if (ecmd->advertising & ADVERTISED_100baseT_Half)
			advertised |= RNP_LINK_SPEED_100_HALF;

		if (ecmd->advertising & ADVERTISED_10baseT_Half)
			advertised |= RNP_LINK_SPEED_10_HALF;

		if (old == advertised)
			return err;

		if ((!advertised) && (autoneg))
			return -EINVAL;

		/* this sets the link speed and restarts auto-neg */
		hw->mac.autotry_restart = true;
		err = hw->ops.setup_link(hw, advertised, autoneg, speed,
					 duplex);
		if (err) {
			e_info(probe, "setup link failed with code %d\n", err);
			hw->ops.setup_link(hw, old, autoneg, speed, duplex);
		}
	} else {
		/* if not sgmii, we not support close autoneg */
		if ((ecmd->autoneg == AUTONEG_DISABLE) && (!hw->is_sgmii))
			return -EINVAL;

		speed = ethtool_cmd_speed(ecmd);

		if (speed != SPEED_1000)
			return -EINVAL;

		if (ecmd->duplex == DUPLEX_HALF)
			return -EINVAL;
	}

	return err;
}

#endif

static int rnpgbe_get_regs_len(struct net_device *netdev)
{
#define RNPGBE_REGS_LEN 1
	return RNPGBE_REGS_LEN * sizeof(u32);
}

static void rnpgbe_get_drvinfo(struct net_device *netdev,
			       struct ethtool_drvinfo *drvinfo)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;

	strscpy(drvinfo->driver, rnpgbe_driver_name, sizeof(drvinfo->driver));
	strscpy(drvinfo->version, rnpgbe_driver_version,
		sizeof(drvinfo->version));

#ifdef FPGA_VESION
	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
		 "fw:0x%x boot: %s",
		 hw->fw_version | (hw->sfc_boot ? 0x8000000 : 0));
#else
	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
		 "%d.%d.%d.%d", ((unsigned char *)&(hw->fw_version))[3],
		 ((unsigned char *)&(hw->fw_version))[2],
		 ((unsigned char *)&(hw->fw_version))[1],
		 ((unsigned char *)&(hw->fw_version))[0]);
#endif
	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
		sizeof(drvinfo->bus_info));
	drvinfo->n_stats = RNPGBE_STATS_LEN;
	drvinfo->testinfo_len = RNPGBE_TEST_LEN;
	drvinfo->regdump_len = rnpgbe_get_regs_len(netdev);
#ifdef HAVE_ETHTOOL_GET_SSET_COUNT
	drvinfo->n_priv_flags = RNPGBE_PRIV_FLAGS_STR_LEN;
#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */
}

static int rnpgbe_get_eeprom_len(struct net_device *netdev)
{
	/* not support this */
	return 0;
}

static int rnpgbe_get_eeprom(struct net_device *netdev,
			     struct ethtool_eeprom *eeprom, u8 *bytes)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;
	int first_u32, last_u32, eeprom_len;
	u32 *eeprom_buff;
	int ret_val = 0;

	if (hw->hw_type == rnpgbe_hw_n210)
		return -EPERM;

	if (eeprom->len == 0)
		return -EINVAL;

	eeprom->magic = hw->vendor_id | (hw->device_id << 16);

	/* assign to u32 */
	first_u32 = eeprom->offset >> 2;
	last_u32 = (eeprom->offset + eeprom->len - 1) >> 2;
	eeprom_len = last_u32 - first_u32 + 1;

	eeprom_buff = kmalloc_array(eeprom_len, sizeof(u32), GFP_KERNEL);
	if (!eeprom_buff)
		return -ENOMEM;

	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 0x03), eeprom->len);
	kfree(eeprom_buff);

	return ret_val;
}

static int rnpgbe_set_eeprom(struct net_device *netdev,
			     struct ethtool_eeprom *eeprom, u8 *bytes)
{
	return 0;
}

static void rnpgbe_get_pauseparam(struct net_device *netdev,
				  struct ethtool_pauseparam *pause)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;

	if (hw->fc.requested_mode & PAUSE_AUTO)
		pause->autoneg = 1;
	else
		pause->autoneg = 0;

	if (hw->fc.current_mode == rnpgbe_fc_rx_pause) {
		pause->rx_pause = 1;
	} else if (hw->fc.current_mode == rnpgbe_fc_tx_pause) {
		pause->tx_pause = 1;
	} else if (hw->fc.current_mode == rnpgbe_fc_full) {
		pause->rx_pause = 1;
		pause->tx_pause = 1;
	} else {
		pause->rx_pause = 0;
		pause->tx_pause = 0;
	}
}

static int rnpgbe_set_pauseparam(struct net_device *netdev,
				 struct ethtool_pauseparam *pause)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;
	struct rnpgbe_fc_info fc = hw->fc;

	fc.disable_fc_autoneg = (pause->autoneg != AUTONEG_ENABLE);
	fc.requested_mode = 0;

	if (pause->autoneg) {
		fc.requested_mode |= PAUSE_AUTO;
	} else {
		if (pause->tx_pause)
			fc.requested_mode |= PAUSE_TX;
		if (pause->rx_pause)
			fc.requested_mode |= PAUSE_RX;
	}
	rnpgbe_mbx_phy_pause_set(hw, fc.requested_mode);

	hw->fc = fc;
	return 0;
}

static void rnpgbe_get_regs(struct net_device *netdev,
			    struct ethtool_regs *regs, void *p)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;
	u32 *regs_buff = p;
	int i;

	memset(p, 0, RNPGBE_REGS_LEN * sizeof(u32));

	for (i = 0; i < RNPGBE_REGS_LEN; i++)
		regs_buff[i] = hw_rd32(hw, i * 4);
}

static int rnpgbe_nway_reset(struct net_device *netdev)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);

	if (netif_running(netdev))
		rnpgbe_reinit_locked(adapter);

	return 0;
}

static void rnpgbe_get_strings(struct net_device *netdev, u32 stringset,
			       u8 *data)
{
	char *p = (char *)data;
	int i;

	switch (stringset) {
	/* maybe we don't support test? */
#ifndef CLOST_SELF_TEST
	case ETH_SS_TEST:
		for (i = 0; i < RNPGBE_TEST_LEN; i++) {
			memcpy(data, rnpgbe_gstrings_test[i], ETH_GSTRING_LEN);
			data += ETH_GSTRING_LEN;
		}
		break;
#endif
	case ETH_SS_STATS:
		for (i = 0; i < RNPGBE_GLOBAL_STATS_LEN; i++) {
			memcpy(p, rnpgbe_gstrings_net_stats[i].stat_string,
			       ETH_GSTRING_LEN);
			p += ETH_GSTRING_LEN;
		}
		for (i = 0; i < RNPGBE_HWSTRINGS_STATS_LEN; i++) {
			memcpy(p, rnpgbe_hwstrings_stats[i].stat_string,
			       ETH_GSTRING_LEN);
			p += ETH_GSTRING_LEN;
		}
		for (i = 0; i < RNP_NUM_TX_QUEUES; i++) {
			/* ====  tx ======== */
			sprintf(p, "---\n     queue%u_tx_packets", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_bytes", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_tx_restart", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_busy", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_done_old", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_clean_desc", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_poll_count", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_irq_more", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_tx_hw_head", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_hw_tail", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_sw_next_to_clean", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_sw_next_to_use", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_send_bytes", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_send_bytes_to_hw", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_todo_update", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_send_done_bytes", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_added_vlan_packets", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_next_to_clean", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_irq_miss", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_tx_equal_count", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_clean_times", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_tx_clean_count", i);
			p += ETH_GSTRING_LEN;

			/* ====  rx ======== */
			sprintf(p, "queue%u_rx_packets", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_bytes", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_driver_drop_packets", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_rx_rsc", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_rsc_flush", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_non_eop_descs", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_alloc_page_failed", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_alloc_buff_failed", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_alloc_page", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_csum_offload_errs", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_csum_offload_good", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_poll_again_count", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_rm_vlan_packets", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_rx_hw_head", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_hw_tail", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_sw_next_to_use", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_sw_next_to_clean", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_rx_next_to_clean", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_irq_miss", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_equal_count", i);
			p += ETH_GSTRING_LEN;

			sprintf(p, "queue%u_rx_clean_times", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_clean_count", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "queue%u_rx_resync", i);
			p += ETH_GSTRING_LEN;
		}

		break;
#ifdef HAVE_ETHTOOL_GET_SSET_COUNT
	case ETH_SS_PRIV_FLAGS:
		memcpy(data, rnpgbe_priv_flags_strings,
		       RNPGBE_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN);
		break;
#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */
	}
}

#ifndef HAVE_ETHTOOL_GET_SSET_COUNT
static int rnpgbe_get_stats_count(struct net_device *netdev)
{
	return RNPGBE_STATS_LEN;
}

#else /* HAVE_ETHTOOL_GET_SSET_COUNT */

static int rnpgbe_get_sset_count(struct net_device *netdev, int sset)
{
	switch (sset) {
	/* now we don't support test */
#ifndef CLOST_SELF_TEST
	case ETH_SS_TEST:
		return RNPGBE_TEST_LEN;
#endif /* CLOST_SELF_TEST */
	case ETH_SS_STATS:
		return RNPGBE_STATS_LEN;
	case ETH_SS_PRIV_FLAGS:
		return RNPGBE_PRIV_FLAGS_STR_LEN;
	default:
		return -EOPNOTSUPP;
	}
}

static u32 rnpgbe_get_priv_flags(struct net_device *netdev)
{
	struct rnpgbe_adapter *adapter =
		(struct rnpgbe_adapter *)netdev_priv(netdev);
	u32 priv_flags = 0;

	//if (adapter->priv_flags & RNP_PRIV_FLAG_MAC_LOOPBACK)
	//	priv_flags |= RNPGBE_MAC_LOOPBACK;
	if (adapter->priv_flags & RNP_PRIV_FLAG_PADDING_DEBUG)
		priv_flags |= RNPGBE_PADDING_DEBUG;
	if (adapter->priv_flags & RNP_PRIV_FLAG_SIMUATE_DOWN)
		priv_flags |= RNPGBE_SIMULATE_DOWN;
	if (adapter->priv_flags & RNP_PRIV_FLAG_ULTRA_SHORT)
		priv_flags |= RNPGBE_ULTRA_SHORT;
	if (adapter->priv_flags & RNP_PRIV_FLAG_DOUBLE_VLAN)
		priv_flags |= RNPGBE_DOUBLE_VLAN;
	if (adapter->priv_flags & RNP_PRIV_FLAG_PAUSE_OWN)
		priv_flags |= RNPGBE_PAUSE_OWN;
	if (adapter->flags2 & RNP_FLAG2_VLAN_STAGS_ENABLED)
		priv_flags |= RNPGBE_STAGS_ENABLE;
	if (adapter->priv_flags & RNP_PRIV_FLAG_JUMBO)
		priv_flags |= RNPGBE_JUMBO_ENABLE;
	if (adapter->priv_flags & RNP_PRIV_FLAG_TX_PADDING)
		priv_flags |= RNPGBE_TX_PADDING;
	if (adapter->priv_flags & RNP_PRIV_FLAG_SOFT_TX_PADDING)
		priv_flags |= RNPGBE_TX_SOLF_PADDING;
	if (adapter->priv_flags & RNP_PRIV_FLAG_REC_HDR_LEN_ERR)
		priv_flags |= RNPGBE_REC_HDR_LEN_ERR;
	if (adapter->priv_flags & RNP_PRIV_FLAG_DOUBLE_VLAN_RECEIVE)
		priv_flags |= RNPGBE_DOUBLE_VLAN_RECEIVE;
	if (adapter->priv_flags & RNP_PRIV_FLAG_RX_SKIP_EN)
		priv_flags |= RNPGBE_RX_SKIP_EN;
	if (adapter->priv_flags & RNP_PRIV_FLAG_TCP_SYNC_PRIO)
		priv_flags |= RNPGBE_TCP_SYNC_PRIO;
	if (adapter->priv_flags & RNP_PRIV_FLAG_REMAP_PRIO)
		priv_flags |= RNPGBE_REMAP_PRIO;
	if (adapter->priv_flags & RNP_PRIV_FLAG_8023_PRIO)
		priv_flags |= RNPGBE_8023_PRIO;
	if (adapter->priv_flags & RNP_PRIV_FLAG_SRIOV_VLAN_MODE)
		priv_flags |= RNPGBE_SRIOV_VLAN_MODE;
	if (adapter->priv_flags & RNP_PRIV_FLAG_LLDP)
		priv_flags |= RNPGBE_LLDP_EN;
	if (adapter->priv_flags & RNP_PRIV_FLAG_LINK_DOWN_ON_CLOSE)
		priv_flags |= RNPGBE_FORCE_CLOSE;

	return priv_flags;
}

static int rnpgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags)
{
	struct rnpgbe_adapter *adapter =
		(struct rnpgbe_adapter *)netdev_priv(netdev);
	struct rnpgbe_hw *hw = &adapter->hw;
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_mac_info *mac = &hw->mac;
	u32 data_old;
	u32 data_new;

	data_old = dma_rd32(dma, RNP_DMA_CONFIG);
	data_new = data_old;
	dbg("data old is %x\n", data_old);

	//if (priv_flags & RNPGBE_MAC_LOOPBACK) {
	//	SET_BIT(rnpgbe_mac_loopback, data_new);
	//	adapter->priv_flags |= RNP_PRIV_FLAG_MAC_LOOPBACK;
	//} else if (adapter->priv_flags & RNP_PRIV_FLAG_MAC_LOOPBACK) {
	//	adapter->priv_flags &= (~RNP_PRIV_FLAG_MAC_LOOPBACK);
	//	CLR_BIT(rnpgbe_mac_loopback, data_new);
	//}

	if (priv_flags & RNPGBE_PADDING_DEBUG)
		adapter->priv_flags |= RNP_PRIV_FLAG_PADDING_DEBUG;
	else if (adapter->priv_flags & RNP_PRIV_FLAG_PADDING_DEBUG)
		adapter->priv_flags &= (~RNP_PRIV_FLAG_PADDING_DEBUG);

	if (priv_flags & RNPGBE_SIMULATE_DOWN) {
		adapter->priv_flags |= RNP_PRIV_FLAG_SIMUATE_DOWN;
		/* set check link again */
		adapter->flags |= RNP_FLAG_NEED_LINK_UPDATE;
	} else if (adapter->priv_flags & RNP_PRIV_FLAG_SIMUATE_DOWN) {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_SIMUATE_DOWN);
		/* set check link again */
		adapter->flags |= RNP_FLAG_NEED_LINK_UPDATE;
	}

	/* if open ultra short function */
	if (priv_flags & RNPGBE_ULTRA_SHORT) {
		int min = 33;

		adapter->priv_flags |= RNP_PRIV_FLAG_ULTRA_SHORT;
		eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_MIN_LEN, min);

	} else {
		int min = 60;

		adapter->priv_flags &= (~RNP_PRIV_FLAG_ULTRA_SHORT);
		eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_MIN_LEN, min);
	}
	if (priv_flags & RNPGBE_PAUSE_OWN) {
		u32 data;

		data = mac_rd32(mac, GMAC_FLOW_CTRL);
		data |= GMAC_FLOW_CTRL_UP;
		adapter->priv_flags |= RNP_PRIV_FLAG_PAUSE_OWN;
		mac_wr32(mac, GMAC_FLOW_CTRL, data);

	} else {
		u32 data;

		data = mac_rd32(mac, GMAC_FLOW_CTRL);
		data &= (~GMAC_FLOW_CTRL_UP);
		adapter->priv_flags &= (~RNP_PRIV_FLAG_PAUSE_OWN);
		mac_wr32(mac, GMAC_FLOW_CTRL, data);
	}

	if (priv_flags & RNPGBE_DOUBLE_VLAN) {
		adapter->priv_flags |= RNP_PRIV_FLAG_DOUBLE_VLAN;
		eth->ops.set_double_vlan(eth, true);
	} else {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_DOUBLE_VLAN);
		eth->ops.set_double_vlan(eth, false);
	}

	if (priv_flags & RNPGBE_STAGS_ENABLE) {
		eth_wr32(eth, RNPGBE_ETH_TX_VLAN_CONTROL_EANBLE, 1);
		adapter->flags2 |= RNP_FLAG2_VLAN_STAGS_ENABLED;
		eth->ops.set_vfta(eth, adapter->stags_vid, true);
	} else {
		int vid = adapter->stags_vid;
		int true_remove = 1;

		eth_wr32(eth, RNPGBE_ETH_TX_VLAN_CONTROL_EANBLE, 0);
		adapter->flags2 &= (~RNP_FLAG2_VLAN_STAGS_ENABLED);
		if (vid) {
#ifndef HAVE_VLAN_RX_REGISTER
			if (test_bit(vid, adapter->active_vlans))
				true_remove = 0;

#ifdef NETIF_F_HW_VLAN_STAG_RX
			if (test_bit(vid, adapter->active_vlans_stags))
				true_remove = 0;
#endif /* NETIF_F_HW_VLAN_STAG_RX */
#endif /* HAVE_VLAN_RX_REGISTER */
			if (true_remove)
				hw->ops.set_vlan_filter(hw, vid, false, false);
		}
	}

	if (priv_flags & RNPGBE_JUMBO_ENABLE) {
		adapter->priv_flags |= RNP_PRIV_FLAG_JUMBO;
		hw->ops.set_mtu(hw, netdev->mtu);
	} else {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_JUMBO);
		hw->ops.set_mtu(hw, netdev->mtu);
	}

	if (priv_flags & RNPGBE_TX_PADDING)
		adapter->priv_flags |= RNP_PRIV_FLAG_TX_PADDING;
	else
		adapter->priv_flags &= (~RNP_PRIV_FLAG_TX_PADDING);

	if (priv_flags & RNPGBE_TX_SOLF_PADDING)
		adapter->priv_flags |= RNP_PRIV_FLAG_SOFT_TX_PADDING;
	else
		adapter->priv_flags &= (~RNP_PRIV_FLAG_SOFT_TX_PADDING);

	if (priv_flags & RNPGBE_REC_HDR_LEN_ERR) {
		adapter->priv_flags |= RNP_PRIV_FLAG_REC_HDR_LEN_ERR;
		eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR, 0);

	} else if (adapter->priv_flags & RNP_PRIV_FLAG_REC_HDR_LEN_ERR) {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_REC_HDR_LEN_ERR);
		eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR,
			 PKT_LEN_ERR | HDR_LEN_ERR);
	}

	if (priv_flags & RNPGBE_DOUBLE_VLAN_RECEIVE) {
		adapter->priv_flags |= RNP_PRIV_FLAG_DOUBLE_VLAN_RECEIVE;
		if (!(adapter->priv_flags & RNP_PRIV_FLAG_RX_ALL))
			eth_wr32(eth, RNPGBE_ETH_DOUBLE_VLAN_DROP, 0);
	} else {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_DOUBLE_VLAN_RECEIVE);
		if (!(adapter->priv_flags & RNP_PRIV_FLAG_RX_ALL))
			eth_wr32(eth, RNPGBE_ETH_DOUBLE_VLAN_DROP, 1);
	}

	if (priv_flags & RNPGBE_TCP_SYNC_PRIO)
		adapter->priv_flags |= RNP_PRIV_FLAG_TCP_SYNC_PRIO;
	else
		adapter->priv_flags &= (~RNP_PRIV_FLAG_TCP_SYNC_PRIO);

	if (priv_flags & RNPGBE_SRIOV_VLAN_MODE) {
		int i;

		adapter->priv_flags |= RNP_PRIV_FLAG_SRIOV_VLAN_MODE;
		if (!(adapter->flags & RNP_FLAG_SRIOV_INIT_DONE))
			goto skip_setup_vf_vlan;
		/* should setup vlvf table */
		for (i = 0; i < adapter->num_vfs; i++) {
			if (!hw->ops.set_vf_vlan_mode)
				break;
			if (adapter->vfinfo[i].vf_vlan)
				hw->ops.set_vf_vlan_mode(hw,
							 adapter->vfinfo[i].vf_vlan,
							 i, true);
			if (adapter->vfinfo[i].pf_vlan)
				hw->ops.set_vf_vlan_mode(hw,
							 adapter->vfinfo[i].pf_vlan,
							 i, true);
		}

	} else if (adapter->priv_flags & RNP_PRIV_FLAG_SRIOV_VLAN_MODE) {
		int i;

		adapter->priv_flags &= (~RNP_PRIV_FLAG_SRIOV_VLAN_MODE);
		/* should clean vlvf table */
		for (i = 0; i < hw->max_vfs; i++) {
			if (!hw->ops.set_vf_vlan_mode)
				break;
			hw->ops.set_vf_vlan_mode(hw, 0, i, false);
		}
	}

	if (priv_flags & RNPGBE_LLDP_EN) {
		/* open lldp */
		hw->ops.set_lldp(hw, true);
		adapter->priv_flags |= RNP_PRIV_FLAG_LLDP;

	} else if (adapter->priv_flags & RNP_PRIV_FLAG_LLDP) {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_LLDP);
		/* close lldp */
		hw->ops.set_lldp(hw, false);
	}

	/* if force close */
	if (hw->force_cap) {
		if (priv_flags & RNPGBE_FORCE_CLOSE) {
			if (!(adapter->priv_flags & RNP_PRIV_FLAG_LINK_DOWN_ON_CLOSE)) {
				adapter->priv_flags |= RNP_PRIV_FLAG_LINK_DOWN_ON_CLOSE;
				if (hw->ops.driver_status)
					hw->ops.driver_status(hw, true,
						rnpgbe_driver_force_control_phy);
			}
		} else if (adapter->priv_flags & RNP_PRIV_FLAG_LINK_DOWN_ON_CLOSE) {
			adapter->priv_flags &= (~RNP_PRIV_FLAG_LINK_DOWN_ON_CLOSE);
			if (hw->ops.driver_status)
				hw->ops.driver_status(hw, false,
						rnpgbe_driver_force_control_phy);

		}
	} else if (priv_flags & RNPGBE_FORCE_CLOSE) {
		rnpgbe_err("firmware not support set this feature.\n");
		return -1;

	}

skip_setup_vf_vlan:
	if (priv_flags & RNPGBE_8023_PRIO) {
		adapter->priv_flags |= RNP_PRIV_FLAG_8023_PRIO;
		eth_wr32(eth, RNPGBE_PRIORITY_EN_8023, 1);
	} else {
		adapter->priv_flags &= (~RNP_PRIV_FLAG_8023_PRIO);
		eth_wr32(eth, RNPGBE_PRIORITY_EN_8023, 0);
	}

	if (priv_flags & RNPGBE_REMAP_PRIO)
		adapter->priv_flags |= RNP_PRIV_FLAG_REMAP_PRIO;
	else
		adapter->priv_flags &= (~RNP_PRIV_FLAG_REMAP_PRIO);

	if (priv_flags & (RNPGBE_8023_PRIO | RNPGBE_REMAP_PRIO)) {
		eth_wr32(eth, RNPGBE_PRIORITY_1_MARK, RNPGBE_PRIORITY_1);
		eth_wr32(eth, RNPGBE_PRIORITY_0_MARK, RNPGBE_PRIORITY_0);
		eth_wr32(eth, RNPGBE_PRIORITY_EN, 1);
	} else {
		eth_wr32(eth, RNPGBE_PRIORITY_EN, 0);
	}

	if (adapter->priv_flags & RNP_PRIV_FLAG_TCP_SYNC) {
		if (adapter->priv_flags & RNP_PRIV_FLAG_TCP_SYNC_PRIO)
			hw->ops.set_tcp_sync_remapping(
				hw, adapter->tcp_sync_queue, true, true);
		else
			hw->ops.set_tcp_sync_remapping(
				hw, adapter->tcp_sync_queue, true, false);
	}

	if (data_old != data_new)
		dma_wr32(dma, RNP_DMA_CONFIG, data_new);
	/* if ft_padding changed */
	if (CHK_BIT(rnpgbe_padding_enable, data_old) !=
	    CHK_BIT(rnpgbe_padding_enable, data_new)) {
		rnpgbe_msg_post_status(adapter, PF_FT_PADDING_STATUS);
	}

	return 0;
}

#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */

static void rnpgbe_get_ethtool_stats(struct net_device *netdev,
				     struct ethtool_stats *stats, u64 *data)
{
	struct rnpgbe_adapter *adapter = netdev_priv(netdev);
	struct net_device_stats *net_stats = &netdev->stats;
	struct rnpgbe_ring *ring;
	char *p = NULL;
	int i, j;

	rnpgbe_update_stats(adapter);

	for (i = 0; i < RNPGBE_GLOBAL_STATS_LEN; i++) {
		p = (char *)net_stats +
		    rnpgbe_gstrings_net_stats[i].stat_offset;
		data[i] = (rnpgbe_gstrings_net_stats[i].sizeof_stat ==
			   sizeof(u64)) ?
				  *(u64 *)p :
				  *(u32 *)p;
	}
	for (j = 0; j < RNPGBE_HWSTRINGS_STATS_LEN; j++, i++) {
		p = (char *)adapter + rnpgbe_hwstrings_stats[j].stat_offset;
		data[i] =
			(rnpgbe_hwstrings_stats[j].sizeof_stat == sizeof(u64)) ?
				*(u64 *)p :
				*(u32 *)p;
	}

	BUG_ON(RNP_NUM_TX_QUEUES != RNP_NUM_RX_QUEUES);

	for (j = 0; j < RNP_NUM_TX_QUEUES; j++) {
		/* tx-ring */
		ring = adapter->tx_ring[j];
		if (!ring) {
			/* tx */
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			/* rx */
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			continue;
		}
		data[i++] = ring->stats.packets;
		data[i++] = ring->stats.bytes;
		data[i++] = ring->tx_stats.restart_queue;
		data[i++] = ring->tx_stats.tx_busy;
		data[i++] = ring->tx_stats.tx_done_old;
		data[i++] = ring->tx_stats.clean_desc;
		data[i++] = ring->tx_stats.poll_count;
		data[i++] = ring->tx_stats.irq_more_count;

		/* rnpgbe_tx_queue_ring_stat */
		data[i++] = ring_rd32(ring, RNP_DMA_REG_TX_DESC_BUF_HEAD);
		data[i++] = ring_rd32(ring, RNP_DMA_REG_TX_DESC_BUF_TAIL);
		data[i++] = ring->next_to_clean;
		data[i++] = ring->next_to_use;
		data[i++] = ring->tx_stats.send_bytes;
		data[i++] = ring->tx_stats.send_bytes_to_hw;
		data[i++] = ring->tx_stats.todo_update;
		data[i++] = ring->tx_stats.send_done_bytes;
		data[i++] = ring->tx_stats.vlan_add;
		if (ring->tx_stats.tx_next_to_clean == -1)
			data[i++] = ring->count;
		else
			data[i++] = ring->tx_stats.tx_next_to_clean;
		data[i++] = ring->tx_stats.tx_irq_miss;
		data[i++] = ring->tx_stats.tx_equal_count;
		data[i++] = ring->tx_stats.tx_clean_times;
		data[i++] = ring->tx_stats.tx_clean_count;

		/* rx-ring */
		ring = adapter->rx_ring[j];
		if (!ring) {
			/* rx */
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			data[i++] = 0;
			continue;
		}
		data[i++] = ring->stats.packets;
		data[i++] = ring->stats.bytes;
		data[i++] = ring->rx_stats.driver_drop_packets;
		data[i++] = ring->rx_stats.rsc_count;
		data[i++] = ring->rx_stats.rsc_flush;
		data[i++] = ring->rx_stats.non_eop_descs;
		data[i++] = ring->rx_stats.alloc_rx_page_failed;
		data[i++] = ring->rx_stats.alloc_rx_buff_failed;
		data[i++] = ring->rx_stats.alloc_rx_page;
		data[i++] = ring->rx_stats.csum_err;
		data[i++] = ring->rx_stats.csum_good;
		data[i++] = ring->rx_stats.poll_again_count;
		data[i++] = ring->rx_stats.vlan_remove;

		/* rnpgbe_rx_queue_ring_stat */
		data[i++] = ring_rd32(ring, RNP_DMA_REG_RX_DESC_BUF_HEAD);
		data[i++] = ring_rd32(ring, RNP_DMA_REG_RX_DESC_BUF_TAIL);
		data[i++] = ring->next_to_use;
		data[i++] = ring->next_to_clean;
		if (ring->rx_stats.rx_next_to_clean == -1)
			data[i++] = ring->count;
		else
			data[i++] = ring->rx_stats.rx_next_to_clean;
		data[i++] = ring->rx_stats.rx_irq_miss;
		data[i++] = ring->rx_stats.rx_equal_count;
		data[i++] = ring->rx_stats.rx_clean_times;
		data[i++] = ring->rx_stats.rx_clean_count;
		data[i++] = ring->rx_stats.rx_resync;
	}
}

static const struct ethtool_ops rnpgbe_ethtool_ops = {
#if defined(ETHTOOL_GLINKSETTINGS) && !defined(KYLIN_V4_ETHTOOL_FIX_BOND)
	.get_link_ksettings = rnpgbe_get_link_ksettings,
	.set_link_ksettings = rnpgbe_set_link_ksettings,
#else
	.get_settings = rnpgbe_get_settings,
	.set_settings = rnpgbe_set_settings,
#endif
	.get_drvinfo = rnpgbe_get_drvinfo,
	.get_regs_len = rnpgbe_get_regs_len,
	.get_regs = rnpgbe_get_regs,
	.get_wol = rnpgbe_get_wol,
	.set_wol = rnpgbe_set_wol,
	.nway_reset = rnpgbe_nway_reset,
	.get_link = ethtool_op_get_link,
	.get_eeprom_len = rnpgbe_get_eeprom_len,
	.get_eeprom = rnpgbe_get_eeprom,
	.set_eeprom = rnpgbe_set_eeprom,
	.get_ringparam = rnpgbe_get_ringparam,
	.set_ringparam = rnpgbe_set_ringparam,
	.get_pauseparam = rnpgbe_get_pauseparam,
	.set_pauseparam = rnpgbe_set_pauseparam,
	.get_msglevel = rnpgbe_get_msglevel,
	.set_msglevel = rnpgbe_set_msglevel,
#ifndef CLOST_SELF_TEST
	.self_test = rnpgbe_diag_test,
#endif
	.get_strings = rnpgbe_get_strings,
#ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT
#ifdef HAVE_ETHTOOL_SET_PHYS_ID
	.set_phys_id = rnpgbe_set_phys_id,
#endif /* HAVE_ETHTOOL_SET_PHYS_ID */
#endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */
#ifndef HAVE_ETHTOOL_GET_SSET_COUNT
	.get_stats_count = rnpgbe_get_stats_count,
#else /* HAVE_ETHTOOL_GET_SSET_COUNT */
	.get_sset_count = rnpgbe_get_sset_count,
	.get_priv_flags = rnpgbe_get_priv_flags,
	.set_priv_flags = rnpgbe_set_priv_flags,
#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */
	.get_ethtool_stats = rnpgbe_get_ethtool_stats,
#ifdef HAVE_ETHTOOL_GET_PERM_ADDR
	.get_perm_addr = ethtool_op_get_perm_addr,
#endif /* HAVE_ETHTOOL_GET_PERM_ADDR */
	.get_coalesce = rnpgbe_get_coalesce,
	.set_coalesce = rnpgbe_set_coalesce,
#ifdef ETHTOOL_COALESCE_USECS
	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
				     ETHTOOL_COALESCE_USE_ADAPTIVE |
				     ETHTOOL_COALESCE_MAX_FRAMES_IRQ |
				     ETHTOOL_COALESCE_MAX_FRAMES,
#endif /* ETHTOOL_COALESCE_USECS */

#ifndef HAVE_NDO_SET_FEATURES
	.get_rx_csum = rnpgbe_get_rx_csum,
	.set_rx_csum = rnpgbe_set_rx_csum,
	.get_tx_csum = ethtool_op_get_tx_csum,
	.set_tx_csum = rnpgbe_set_tx_csum,
	.get_sg = ethtool_op_get_sg,
	.set_sg = ethtool_op_set_sg,
#ifdef NETIF_F_TSO
	.get_tso = ethtool_op_get_tso,
	.set_tso = rnpgbe_set_tso,
#endif /* NETIF_F_TSO */
#ifdef ETHTOOL_GFLAGS
	.get_flags = ethtool_op_get_flags,
#endif
#endif /* HAVE_NDO_SET_FEATURES */
#ifdef ETHTOOL_GRXRINGS
	.get_rxnfc = rnpgbe_get_rxnfc,
	.set_rxnfc = rnpgbe_set_rxnfc,
#endif

#ifdef ETHTOOL_SRXNTUPLE
	.set_rx_ntuple = rnpgbe_set_rx_ntuple,
#endif
#ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT
#ifdef ETHTOOL_GEEE
#ifdef HAVE_ETHTOOL_KEEE
	.get_eee = rnpgbe_get_keee,
#else
	.get_eee = rnpgbe_get_eee,
#endif /* HAVE_ETHTOOL_KEEE */
#endif /* ETHTOOL_GEEE */
#ifdef ETHTOOL_SEEE
#ifdef HAVE_ETHTOOL_KEEE
	.set_eee = rnpgbe_set_keee,
#else
	.set_eee = rnpgbe_set_eee,
#endif /* HAVE_ETHTOOL_KEEE */
#endif /* ETHTOOL_SEEE */
#ifdef ETHTOOL_SCHANNELS
	.get_channels = rnpgbe_get_channels,
	.set_channels = rnpgbe_set_channels,
#endif
#ifdef ETHTOOL_GMODULEINFO
	.get_module_info = rnpgbe_get_module_info,
	.get_module_eeprom = rnpgbe_get_module_eeprom,
#endif
#ifdef HAVE_ETHTOOL_GET_TS_INFO
	.get_ts_info = rnpgbe_get_ts_info,
#endif
#if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH)
	.get_rxfh_indir_size = rnpgbe_rss_indir_size,
	.get_rxfh_key_size = rnpgbe_get_rxfh_key_size,
	.get_rxfh = rnpgbe_get_rxfh,
	.set_rxfh = rnpgbe_set_rxfh,
#endif /* ETHTOOL_GRSSH && ETHTOOL_SRSSH */
	.get_dump_flag = rnpgbe_get_dump_flag,
	.get_dump_data = rnpgbe_get_dump_data,
	.set_dump = rnpgbe_set_dump,
#endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */

#if defined(HAVE_DDP_PROFILE_UPLOAD_SUPPORT) || defined(CONFIG_LINX_SERIAL)
	.flash_device = rnpgbe_flash_device,
#endif /* HAVE_DDP_PROFILE_UPLOAD_SUPPORT */
};

static void rnpgbe_set_ethtool_hw_ops(struct net_device *netdev)
{
#ifndef ETHTOOL_OPS_COMPAT
	netdev->ethtool_ops = &rnpgbe_ethtool_ops;
#else
	SET_ETHTOOL_OPS(netdev, &rnpgbe_ethtool_ops);
#endif
}

static struct rnpgbe_hw_operations hw_ops_rnpgbe = {
	.init_hw = &rnpgbe_init_hw_ops,
	.reset_hw = &rnpgbe_reset_hw_ops,
	.start_hw = &rnpgbe_start_hw_ops,
	.set_mtu = &rnpgbe_set_mtu_hw_ops,
	.set_vlan_filter_en = &rnpgbe_set_vlan_filter_en_hw_ops,
	.set_vlan_filter = &rnpgbe_set_vlan_filter_hw_ops,
	.set_veb_vlan_mask = &rnpgbe_set_veb_vlan_mask_hw_ops,
	.set_vf_vlan_filter = &rnpgbe_set_vf_vlan_filter_hw_ops,
	.set_vlan_strip = &rnpgbe_set_vlan_strip_hw_ops,
	.set_mac = &rnpgbe_set_mac_hw_ops,
	.set_rx_mode = &rnpgbe_set_rx_mode_hw_ops,
	.set_rar_with_vf = &rnpgbe_set_rar_with_vf_hw_ops,
	.clr_rar = &rnpgbe_clr_rar_hw_ops,
	.clr_rar_all = &rnpgbe_clr_rar_all_hw_ops,
	.clr_vlan_veb = &rnpgbe_clr_vlan_veb_hw_ops,
	.set_txvlan_mode = &rnpgbe_set_txvlan_mode_hw_ops,
	.set_fcs_mode = &rnpgbe_set_fcs_mode_hw_ops,
	.set_mac_rx = &rnpgbe_set_mac_rx_hw_ops,
	.set_mac_speed = &rnpgbe_set_mac_speed_hw_ops,
	.set_rx_hash = &rnpgbe_set_rx_hash_hw_ops,
	.set_pause_mode = &rnpgbe_set_pause_mode_hw_ops,
	.get_pause_mode = &rnpgbe_get_pause_mode_hw_ops,
	.update_hw_info = &rnpgbe_update_hw_info_hw_ops,
	.update_rx_drop = &rnpgbe_update_hw_rx_drop_hw_ops,
	.update_sriov_info = &rnpgbe_update_sriov_info_hw_ops,
	.set_sriov_status = &rnpgbe_set_sriov_status_hw_ops,
	.set_sriov_vf_mc = &rnpgbe_set_sriov_vf_mc_hw_ops,
	.init_rx_addrs = &rnpgbe_init_rx_addrs_hw_ops,
	.clr_vfta = &rnpgbe_clr_vfta_hw_ops,
	.set_rss_hfunc = &rnpgbe_set_rss_hfunc_hw_ops,
	.set_rss_key = &rnpgbe_set_rss_key_hw_ops,
	.set_rss_table = &rnpgbe_set_rss_table_hw_ops,
	.update_hw_status = &rnpgbe_update_hw_status_hw_ops,
	.set_mbx_link_event = &rnpgbe_set_mbx_link_event_hw_ops,
	.set_mbx_ifup = &rnpgbe_set_mbx_ifup_hw_ops,
	.check_link = &rnpgbe_check_mac_link_hw_ops,
	.setup_link = &rnpgbe_setup_mac_link_hw_ops,
	.clean_link = &rnpgbe_clean_link_hw_ops,
	.set_layer2_remapping = &rnpgbe_set_layer2_hw_ops,
	.clr_layer2_remapping = &rnpgbe_clr_layer2_hw_ops,
	.clr_all_layer2_remapping = &rnpgbe_clr_all_layer2_hw_ops,
	.set_tuple5_remapping = &rnpgbe_set_tuple5_hw_ops,
	.clr_tuple5_remapping = &rnpgbe_clr_tuple5_hw_ops,
	.clr_all_tuple5_remapping = &rnpgbe_clr_all_tuple5_hw_ops,
	.set_tcp_sync_remapping = &rnpgbe_set_tcp_sync_hw_ops,
	.set_rx_skip = &rnpgbe_set_rx_skip_hw_ops,
	.set_outer_vlan_type = &rnpgbe_set_outer_vlan_type_hw_ops,
	.setup_ethtool = &rnpgbe_set_ethtool_hw_ops,
	.get_thermal_sensor_data = &rnpgbe_get_thermal_sensor_data_hw_ops,
	.init_thermal_sensor_thresh =
		&rnpgbe_init_thermal_sensor_thresh_hw_ops,
	.phy_read_reg = &rnpgbe_phy_read_reg_hw_ops,
	.phy_write_reg = &rnpgbe_phy_write_reg_hw_ops,
	.setup_wol = &rnpgbe_setup_wol_hw_ops,
	.set_vf_vlan_mode = &rnpgbe_set_vf_vlan_mode_hw_ops,
	.driver_status = &rnpgbe_driver_status_hw_ops,
	.setup_eee = &rnpgbe_setup_eee_hw_ops,
	.set_eee_mode = &rnpgbe_set_eee_mode_hw_ops,
	.reset_eee_mode = &rnpgbe_reset_eee_mode_hw_ops,
	.set_eee_timer = &rnpgbe_set_eee_timer_hw_ops,
	.set_eee_pls = &rnpgbe_set_eee_pls_hw_ops,
	.get_lpi_status = &rnpgbe_get_lpi_status_hw_ops,
	.get_ncsi_mac = &rnpgbe_get_ncsi_mac_hw_ops,
	.get_ncsi_vlan = &rnpgbe_get_ncsi_vlan_hw_ops,
	.set_lldp = &rnpgbe_set_lldp_hw_ops,
	.get_lldp = &rnpgbe_get_lldp_hw_ops,
	.dump_debug_regs = &rnpgbe_dump_debug_regs_hw_ops,
};

static void gmac_set_rx(struct rnpgbe_mac_info *mac, bool status)
{

	u32 value = mac_rd32(mac, GMAC_CONTROL);

	if (status)
		value |= GMAC_CONTROL_TE | GMAC_CONTROL_RE;
	else
		value &= ~(GMAC_CONTROL_RE);

	mac_wr32(mac, GMAC_CONTROL, value);
	value = mac_rd32(mac, GMAC_FRAME_FILTER);
	mac_wr32(mac, GMAC_FRAME_FILTER, value | 1);
}

static void gmac_set_speed(struct rnpgbe_mac_info *mac, bool link,
			   u32 speed, bool duplex)
{

}

static void gmac_fcs(struct rnpgbe_mac_info *mac, bool status)
{
#define RNPGBE_CST_MASK BIT(25)
	u32 value = mac_rd32(mac, GMAC_CONTROL);

	if (status)
		value &= (~RNPGBE_CST_MASK);
	else
		value |= (RNPGBE_CST_MASK);
	mac_wr32(mac, GMAC_CONTROL, value);
}

/**
 *  gmac_fc_mode - Enable flow control
 *  @mac: pointer to mac structure
 *
 *  Enable flow control according to the current settings.
 **/
static s32 gmac_fc_mode(struct rnpgbe_mac_info *mac)
{
	struct rnpgbe_hw *hw = (struct rnpgbe_hw *)mac->back;
	unsigned int flow = GMAC_FLOW_CTRL_UP;
	s32 ret_val = 0;

	flow = mac_rd32(mac, GMAC_FLOW_CTRL);
	flow &= GMAC_FLOW_CTRL_UP;

	/*
	 * Validate the water mark configuration for packet buffer 0.  Zero
	 * water marks indicate that the packet buffer was not configured
	 * and the watermarks for packet buffer 0 should always be configured.
	 */
	if (!hw->fc.pause_time) {
		ret_val = RNP_ERR_INVALID_LINK_SETTINGS;
		goto out;
	}

	switch (hw->fc.current_mode) {
	case rnpgbe_fc_none:
		/*
		 * Flow control is disabled by software override or autoneg.
		 * The code below will actually disable it in the HW.
		 */
		break;
	case rnpgbe_fc_rx_pause:
		/*
		 * Rx Flow control is enabled and Tx Flow control is
		 * disabled by software override. Since there really
		 * isn't a way to advertise that we are capable of RX
		 * Pause ONLY, we will advertise that we support both
		 * symmetric and asymmetric Rx PAUSE.  Later, we will
		 * disable the adapter's ability to send PAUSE frames.
		 */
		flow |= GMAC_FLOW_CTRL_RFE;
		break;
	case rnpgbe_fc_tx_pause:
		/*
		 * Tx Flow control is enabled, and Rx Flow control is
		 * disabled by software override.
		 */
		flow |= GMAC_FLOW_CTRL_TFE;
		break;
	case rnpgbe_fc_full:
		/* Flow control (both Rx and Tx) is enabled by SW override. */
		flow |= GMAC_FLOW_CTRL_RFE;
		flow |= GMAC_FLOW_CTRL_TFE;
		break;
	default:
		hw_dbg(hw, "Flow control param set incorrectly\n");
		ret_val = RNP_ERR_CONFIG;
		goto out;
	}

	flow |= (hw->fc.pause_time << GMAC_FLOW_CTRL_PT_SHIFT);
	mac_wr32(mac, GMAC_FLOW_CTRL, flow);

out:
	return ret_val;
}

static bool poll_free_mdio(u8 __iomem *addr, u32 mask, int count)
{
	unsigned int value;
	int con = 0;

	do {
		value = rnpgbe_rd_reg(addr);
		usleep_range(10, 100);
		con++;
	} while ((value & mask) && (con < count));

	return !!(con >= count);
}

static int rnpgbe_mdio_read(struct rnpgbe_mac_info *mac, int phyreg)
{
#define MII_BUSY 0x00000001
#define MII_WRITE 0x00000002
#define MII_DATA_MASK GENMASK(15, 0)

	unsigned int mii_address = mac->mii.addr;
	unsigned int mii_data = mac->mii.data;
	u32 value = MII_BUSY;
	int data = 0;
	int phyaddr = mac->phy_addr;

	value |= (phyaddr << mac->mii.addr_shift) & mac->mii.addr_mask;
	value |= (phyreg << mac->mii.reg_shift) & mac->mii.reg_mask;
	value |= (mac->clk_csr << mac->mii.clk_csr_shift) &
		 mac->mii.clk_csr_mask;

	if (poll_free_mdio(mac->mac_addr + mii_address, MII_BUSY, 100))
		return -EBUSY;

	mac_wr32(mac, mii_data, data);
	mac_wr32(mac, mii_address, value);

	if (poll_free_mdio(mac->mac_addr + mii_address, MII_BUSY, 100))
		return -EBUSY;
	/* Read the data from the MII data register */
	data = (int)mac_rd32(mac, mii_data) & MII_DATA_MASK;

	return data;
}

static void gmac_check_link(struct rnpgbe_mac_info *mac,
			    rnpgbe_link_speed *speed,
			    bool *link_up,
			    bool link_up_wait_to_complete)
{
	struct rnpgbe_hw *hw = (struct rnpgbe_hw *)mac->back;
	/* always assume link is up, if no check link function */
	u32 data;
#ifdef CONFIG_RNP_FPGA
#define LINK_IS_UP (0x04)
#define TEST_PHY (LINK_IS_UP)
#else
#define AUTONEGOTATION_COMPLETE (0x20)
#define LINK_IS_UP (0x04)
#define TEST_PHY (AUTONEGOTATION_COMPLETE | LINK_IS_UP)
#endif

	data = rnpgbe_mdio_read(mac, 1);
	if ((data & TEST_PHY) == TEST_PHY) {
		data = rnpgbe_mdio_read(mac, 0);
#define DUPLEX_MODE (0x100)
		if (data & DUPLEX_MODE) {
			if (data & 0x40) {
				*speed = RNP_LINK_SPEED_1GB_FULL;
				hw->speed = SPEED_1000;
			} else if (data & 0x2000) {
				*speed = RNP_LINK_SPEED_100_FULL;
				hw->speed = SPEED_100;
			} else {
				*speed = RNP_LINK_SPEED_10_FULL;
				hw->speed = SPEED_10;
			}
		} else {
			if (data & 0x40) {
				*speed = RNP_LINK_SPEED_1GB_HALF;
				hw->speed = SPEED_1000;
			} else if (data & 0x2000) {
				*speed = RNP_LINK_SPEED_100_HALF;
				hw->speed = SPEED_100;
			} else {
				*speed = RNP_LINK_SPEED_10_HALF;
				hw->speed = SPEED_10;
			}
		}
		*link_up = true;
		hw->link = true;
	} else {
		*link_up = false;
		hw->link = false;
		*speed = RNP_LINK_SPEED_UNKNOWN;
	}
}

static void gmac_set_mac(struct rnpgbe_mac_info *mac,
			 u8 *addr, int index)
{
	u32 rar_low, rar_high = 0;

	rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) | ((u32)addr[2] << 16) |
		   ((u32)addr[3] << 24));
	rar_high = RNP_RAH_AV | ((u32)addr[4] | (u32)addr[5] << 8);
	mac_wr32(mac, GMAC_MAC_UNICAST_HIGH(index), rar_high);
	mac_wr32(mac, GMAC_MAC_UNICAST_LOW(index), rar_low);
}

static int gmac_mdio_read(struct rnpgbe_mac_info *mac,
			  u32 phyreg,
			  u32 *regvalue)
{
	unsigned int mii_address = mac->mii.addr;
	unsigned int mii_data = mac->mii.data;
	int phyaddr = mac->phy_addr;
	u32 value = MII_BUSY;
	int data = 0;

	value |= (phyaddr << mac->mii.addr_shift) & mac->mii.addr_mask;
	value |= (phyreg << mac->mii.reg_shift) & mac->mii.reg_mask;
	value |= (mac->clk_csr << mac->mii.clk_csr_shift) &
		 mac->mii.clk_csr_mask;

	if (poll_free_mdio(mac->mac_addr + mii_address, MII_BUSY, 100))
		return -EBUSY;

	mac_wr32(mac, mii_data, data);
	mac_wr32(mac, mii_address, value);

	if (poll_free_mdio(mac->mac_addr + mii_address, MII_BUSY, 100))
		return -EBUSY;
	/* Read the data from the MII data register */
	data = (int)mac_rd32(mac, mii_data) & MII_DATA_MASK;

	*regvalue = data;

	return data;
}

static int gmac_mdio_write(struct rnpgbe_mac_info *mac,
			   int phyreg,
			   int phydata)
{
	unsigned int mii_address = mac->mii.addr;
	unsigned int mii_data = mac->mii.data;
	int phyaddr = mac->phy_addr;
	u32 value = MII_BUSY;
	int data = phydata;

	value |= (phyaddr << mac->mii.addr_shift) & mac->mii.addr_mask;
	value |= (phyreg << mac->mii.reg_shift) & mac->mii.reg_mask;

	value |= (mac->clk_csr << mac->mii.clk_csr_shift) &
		 mac->mii.clk_csr_mask;
	value |= MII_WRITE;

	/* Wait until any existing MII operation is complete */
	if (poll_free_mdio(mac->mac_addr + mii_address, MII_BUSY, 100))
		return -EBUSY;
	/* Set the MII address register to write */
	mac_wr32(mac, mii_data, data);
	mac_wr32(mac, mii_address, value);

	/* Wait until any existing MII operation is complete */
	return poll_free_mdio(mac->mac_addr + mii_address, MII_BUSY, 100);
}

static void gmac_pmt(struct rnpgbe_mac_info *mac,
		     u32 mode, bool ncsi_en)
{
	unsigned int pmt = 0;

	if (mode & RNP_WUFC_MAG) {
		rnpgbe_dbg("GMAC: WOL Magic frame\n");
		pmt |= magic_pkt_en;
	}
	if (mode & RNP_WUFC_EX) {
		rnpgbe_dbg("GMAC: WOL on global unicast\n");
		pmt |= global_unicast | wake_up_frame_en;
	}
	/* only pmt down not ocp */
	if (!ncsi_en)
		pmt |= power_down;

	mac_wr32(mac, GMAC_PMT, pmt);
}

static void gmac_set_eee_mode(struct rnpgbe_mac_info *mac,
			      bool en_tx_lpi_clockgating)
{
	u32 value = 0;

	/*TODO - en_tx_lpi_clockgating treatment */

	/*
	 * Enable the link status receive on RGMII, SGMII ore SMII
	 * receive path and instruct the transmit to enter in LPI
	 * state.
	 */
	value |= LPI_CTRL_STATUS_PLS;
	value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
	mac_wr32(mac, GMAC_LPI_CTRL_STATUS, value);
}

static void gmac_reset_eee_mode(struct rnpgbe_mac_info *mac)
{
	u32 value = 0;

	value |= LPI_CTRL_STATUS_PLS;
	value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
	mac_wr32(mac, GMAC_LPI_CTRL_STATUS, value);
}

static void gmac_set_eee_timer(struct rnpgbe_mac_info *mac,
			       int ls, int tw)
{
	int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);

	/* Program the timers in the LPI timer control register:
	 * LS: minimum time (ms) for which the link
	 *  status from PHY should be ok before transmitting
	 *  the LPI pattern.
	 * TW: minimum time (us) for which the core waits
	 *  after it has stopped transmitting the LPI pattern.
	 */
	mac_wr32(mac, GMAC_LPI_TIMER_CTRL, value);
}

static void gmac_set_eee_pls(struct rnpgbe_mac_info *mac, int link)
{
	u32 value = 0;

	value = mac_rd32(mac, GMAC_LPI_CTRL_STATUS);

	if (link)
		value |= LPI_CTRL_STATUS_PLS;
	else
		value &= ~LPI_CTRL_STATUS_PLS;

	mac_wr32(mac, GMAC_LPI_CTRL_STATUS, value);
}

static u32 gmac_get_lpi_status(struct rnpgbe_mac_info *mac)
{
	if (mac_rd32(mac, GMAC_INT_STATUS) & GMAC_INT_STATUS_LPIIS)
		return mac_rd32(mac, GMAC_LPI_CTRL_STATUS);
	else
		return 0;
}

static struct rnpgbe_mac_operations mac_ops_gmac = {
	.set_mac_rx = &gmac_set_rx,
	.set_mac_speed = &gmac_set_speed,
	.set_mac_fcs = &gmac_fcs,
	.set_fc_mode = &gmac_fc_mode,
	.check_link = &gmac_check_link,
	.set_mac = &gmac_set_mac,
	.mdio_write = &gmac_mdio_write,
	.mdio_read = &gmac_mdio_read,
	.pmt = &gmac_pmt,
	.set_eee_mode = gmac_set_eee_mode,
	.reset_eee_mode = gmac_reset_eee_mode,
	.set_eee_timer = gmac_set_eee_timer,
	.set_eee_pls = gmac_set_eee_pls,
	.get_lpi_status = gmac_get_lpi_status,
};

static void rnpgbe_get_invariants_common(struct rnpgbe_hw *hw)
{
	struct rnpgbe_adapter *adapter = (struct rnpgbe_adapter *)hw->back;
	struct rnpgbe_mac_info *mac = &hw->mac;
	struct rnpgbe_dma_info *dma = &hw->dma;
	struct rnpgbe_eth_info *eth = &hw->eth;
	struct rnpgbe_nic_info *nic = &hw->nic;
	struct rnpgbe_mbx_info *mbx = &hw->mbx;
	int i;

	nic->nic_base_addr = hw->hw_addr + RNPGBE_NIC_BASE;
	/* setup dma info */
	dma->dma_base_addr = hw->hw_addr;
	dma->dma_ring_addr = hw->hw_addr + RNPGBE_RING_BASE;
	dma->max_tx_queues = RNPGBE_MAX_TX_QUEUES;
	dma->max_rx_queues = RNPGBE_MAX_RX_QUEUES;
	dma->back = hw;
	memcpy(&hw->dma.ops, &dma_ops_rnpgbe, sizeof(hw->dma.ops));

	/* setup eth info */
	memcpy(&hw->eth.ops, &eth_ops_rnpgbe, sizeof(hw->eth.ops));

	eth->eth_base_addr = hw->hw_addr + RNPGBE_ETH_BASE;
	eth->back = hw;
	eth->mc_filter_type = 0;
	eth->mcft_size = RNPGBE_MC_TBL_SIZE;
	eth->vft_size = RNPGBE_VFT_TBL_SIZE;
	eth->num_rar_entries = RNPGBE_RAR_ENTRIES + NCSI_RAR_NUM;
	eth->max_rx_queues = RNPGBE_MAX_RX_QUEUES;
	eth->max_tx_queues = RNPGBE_MAX_TX_QUEUES;

	/* setup mac info */
	memcpy(&hw->mac.ops, &mac_ops_gmac, sizeof(hw->mac.ops));
	mac->mac_addr = hw->hw_addr + RNPGBE_MAC_BASE;
	mac->back = hw;
	mac->mac_type = mac_dwc_g;
	/* move this to eth todo */
	mac->mc_filter_type = 0;
	mac->mcft_size = 2;
	mac->vft_size = 1;
	mac->num_rar_entries = RNPGBE_RAR_ENTRIES;
	mac->max_rx_queues = RNPGBE_MAX_RX_QUEUES;
	mac->max_tx_queues = RNPGBE_MAX_TX_QUEUES;
	mac->max_msix_vectors = RNPGBE_MSIX_VECTORS;
	mac->mii.addr = GMAC_MII_ADDR;
	mac->mii.data = GMAC_MII_DATA;
	mac->mii.addr_shift = 11;
	mac->mii.addr_mask = 0x0000F800;
	mac->mii.reg_shift = 6;
	mac->mii.reg_mask = 0x000007C0;
	mac->mii.clk_csr_shift = 2;
	mac->mii.clk_csr_mask = GENMASK(5, 2);
	mac->clk_csr = 0x02; /* csr 25M */

	mac->phy_addr = 0x11;

	hw->feature_flags |=
		RNP_NET_FEATURE_SG | RNP_NET_FEATURE_TX_CHECKSUM |
		RNP_NET_FEATURE_RX_CHECKSUM | RNP_NET_FEATURE_TSO |
		RNP_NET_FEATURE_VLAN_FILTER | RNP_NET_FEATURE_VLAN_OFFLOAD |
		RNP_NET_FEATURE_RX_NTUPLE_FILTER | RNP_NET_FEATURE_RX_HASH |
		RNP_NET_FEATURE_USO | RNP_NET_FEATURE_RX_FCS |
		RNP_NET_FEATURE_STAG_FILTER | RNP_NET_FEATURE_STAG_OFFLOAD;
	/* maybe supported future*/
	hw->feature_flags |= RNP_HW_FEATURE_EEE;

	/* setup some fdir resource */
	hw->min_length = RNPGBE_MIN_MTU;
	hw->max_length = RNPGBE_MAX_JUMBO_FRAME_SIZE;
	hw->max_msix_vectors = RNPGBE_MSIX_VECTORS;
	hw->num_rar_entries = RNPGBE_RAR_ENTRIES;
	hw->fdir_mode = fdir_mode_tuple5;
	hw->max_vfs = RNPGBE_MAX_VF;
	hw->layer2_count = RNPGBE_MAX_LAYER2_FILTERS - 1;
	hw->tuple5_count = RNPGBE_MAX_TUPLE5_FILTERS - 1;

	/* rnpgbe support magic wol */
	hw->wol_supported = WAKE_MAGIC;

	hw->num_vebvlan_entries = 8;
	hw->default_rx_queue = 0;
	hw->rss_indir_tbl_num = RNPGBE_RSS_TBL_NUM;
	hw->rss_tc_tbl_num = RNPGBE_RSS_TC_TBL_NUM;
	/* vf use the last vfnum */
	hw->vfnum = RNPGBE_MAX_VF - 1;

	hw->sriov_ring_limit = 1;
	hw->max_pf_macvlans = RNPGBE_MAX_PF_MACVLANS;
	hw->veb_ring = RNPGBE_MAX_RX_QUEUES - 1;
	memcpy(&hw->ops, &hw_ops_rnpgbe, sizeof(hw->ops));
	hw->supported_link = RNP_LINK_SPEED_1GB_FULL;
	mbx->mbx_feature |= MBX_FEATURE_NO_ZERO;

	memcpy(&hw->mbx.ops, &rnpgbe_mbx_ops_generic , sizeof(hw->mbx.ops));

	adapter->priv_flags |= RNP_PRIV_FLAG_PAUSE_OWN;
	adapter->drop_time = 100;

	hw->msix_vector_base = 0x28000;

	/* initialization default pause flow */
	hw->fc.requested_mode = PAUSE_AUTO;
	hw->fc.pause_time = RNP_DEFAULT_FCPAUSE;
	hw->autoneg = 1;

	/* we start from auto mode */
#ifdef ETH_TP_MDI_AUTO
	hw->tp_mdix_ctrl = ETH_TP_MDI_AUTO;
#endif
	for (i = 0; i < RNP_MAX_TRAFFIC_CLASS; i++) {
		hw->fc.high_water[i] = RNPGBE_DEFAULT_HIGH_WATER;
		hw->fc.low_water[i] = RNPGBE_DEFAULT_LOW_WATER;
	}
	hw->eeprom.word_size = 10;
}

static s32 rnpgbe_get_invariants_n500(struct rnpgbe_hw *hw)
{
	struct rnpgbe_mbx_info *mbx = &hw->mbx;

	rnpgbe_get_invariants_common(hw);
	if (!hw->axi_mhz)
		hw->usecstocount = 125;
	else
		hw->usecstocount = hw->axi_mhz;

	hw->max_vfs_noari = 1;

	/* mbx setup */
	mbx->vf2pf_mbox_vec_base = 0x28900;
	mbx->cpu2pf_mbox_vec = 0x28b00;
	mbx->pf_vf_shm_base = 0x29000;
	mbx->mbx_mem_size = 64;
	mbx->pf2vf_mbox_ctrl_base = 0x2a100;
	mbx->pf_vf_mbox_mask_lo = 0x2a200;
	mbx->pf_vf_mbox_mask_hi = 0;
	mbx->cpu_pf_shm_base = 0x2d000;
	mbx->pf2cpu_mbox_ctrl = 0x2e000;
	mbx->cpu_pf_mbox_mask = 0x2e200;
	mbx->cpu_vf_share_ram = 0x2b000;
	mbx->share_size = 512;

	hw->msix_vector_base = 0x28200;

	return 0;
}

static s32 rnpgbe_get_invariants_n210(struct rnpgbe_hw *hw)
{
	struct rnpgbe_mbx_info *mbx = &hw->mbx;

	rnpgbe_get_invariants_common(hw);

	if (!hw->axi_mhz)
		hw->usecstocount = 62;
	else
		hw->usecstocount = hw->axi_mhz;

	/* n210 only 1 pf, can open 7 in no ari */
	hw->max_vfs_noari = 7;

	/* mbx setup */
	mbx->vf2pf_mbox_vec_base = 0x29200;
	mbx->cpu2pf_mbox_vec = 0x29400;
	mbx->pf_vf_shm_base = 0x29900;
	mbx->mbx_mem_size = 64;
	mbx->pf2vf_mbox_ctrl_base = 0x2aa00;
	mbx->pf_vf_mbox_mask_lo = 0x2ab00;
	mbx->pf_vf_mbox_mask_hi = 0;
	mbx->cpu_pf_shm_base = 0x2d900;
	mbx->pf2cpu_mbox_ctrl = 0x2e900;
	mbx->cpu_pf_mbox_mask = 0x2eb00;
	mbx->cpu_vf_share_ram = 0x2b900;
	mbx->share_size = 512;

	hw->msix_vector_base = 0x28000;

	return 0;
}

struct rnpgbe_info rnpgbe_n500_info = {
	.total_queue_pair_cnts = RNPGBE_MAX_TX_QUEUES,
	.rss_type = rnpgbe_rss_n500,
	.hw_type = rnpgbe_hw_n500,
	.get_invariants = &rnpgbe_get_invariants_n500,
};

struct rnpgbe_info rnpgbe_n210_info = {
	.total_queue_pair_cnts = RNPGBE_MAX_TX_QUEUES,
	.rss_type = rnpgbe_rss_n500,
	.hw_type = rnpgbe_hw_n210,
	.get_invariants = &rnpgbe_get_invariants_n210,
};

struct rnpgbe_info rnpgbe_n210L_info = {
	.total_queue_pair_cnts = RNPGBE_MAX_TX_QUEUES,
	.rss_type = rnpgbe_rss_n500,
	.hw_type = rnpgbe_hw_n210L,
	.get_invariants = &rnpgbe_get_invariants_n210,
};
